roojs-all.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         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * 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.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * 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]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 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
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * 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>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @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..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @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'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @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'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @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'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @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'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to 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     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @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'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 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).
4882 <p>
4883 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>
4884
4885 <p>
4886 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.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <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>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <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>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * 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;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @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).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * 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)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165         
7166         /**
7167          * Looks at  the scrollable parent element
7168          */
7169         findScrollableParent : function(){
7170             
7171             var el = Roo.get(this.dom.parentNode);
7172             
7173             while (el && !el.isScrollable() && el.dom.nodeName.toLowerCase() != 'body'){
7174                 el = Roo.get(el.dom.parentNode);
7175             }
7176             
7177             if(!el.isScrollable()){
7178                 return null;
7179             }
7180             
7181             return el;
7182         },
7183
7184         /**
7185          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7186          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7187          * @param {String} selector The simple selector to test
7188          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7189                 search as a number or element (defaults to 10 || document.body)
7190          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7191          */
7192         up : function(simpleSelector, maxDepth){
7193             return this.findParentNode(simpleSelector, maxDepth, true);
7194         },
7195
7196
7197
7198         /**
7199          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7200          * @param {String} selector The simple selector to test
7201          * @return {Boolean} True if this element matches the selector, else false
7202          */
7203         is : function(simpleSelector){
7204             return Roo.DomQuery.is(this.dom, simpleSelector);
7205         },
7206
7207         /**
7208          * Perform animation on this element.
7209          * @param {Object} args The YUI animation control args
7210          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7211          * @param {Function} onComplete (optional) Function to call when animation completes
7212          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7213          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7214          * @return {Roo.Element} this
7215          */
7216         animate : function(args, duration, onComplete, easing, animType){
7217             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7218             return this;
7219         },
7220
7221         /*
7222          * @private Internal animation call
7223          */
7224         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7225             animType = animType || 'run';
7226             opt = opt || {};
7227             var anim = Roo.lib.Anim[animType](
7228                 this.dom, args,
7229                 (opt.duration || defaultDur) || .35,
7230                 (opt.easing || defaultEase) || 'easeOut',
7231                 function(){
7232                     Roo.callback(cb, this);
7233                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7234                 },
7235                 this
7236             );
7237             opt.anim = anim;
7238             return anim;
7239         },
7240
7241         // private legacy anim prep
7242         preanim : function(a, i){
7243             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7244         },
7245
7246         /**
7247          * Removes worthless text nodes
7248          * @param {Boolean} forceReclean (optional) By default the element
7249          * keeps track if it has been cleaned already so
7250          * you can call this over and over. However, if you update the element and
7251          * need to force a reclean, you can pass true.
7252          */
7253         clean : function(forceReclean){
7254             if(this.isCleaned && forceReclean !== true){
7255                 return this;
7256             }
7257             var ns = /\S/;
7258             var d = this.dom, n = d.firstChild, ni = -1;
7259             while(n){
7260                 var nx = n.nextSibling;
7261                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7262                     d.removeChild(n);
7263                 }else{
7264                     n.nodeIndex = ++ni;
7265                 }
7266                 n = nx;
7267             }
7268             this.isCleaned = true;
7269             return this;
7270         },
7271
7272         // private
7273         calcOffsetsTo : function(el){
7274             el = Roo.get(el);
7275             var d = el.dom;
7276             var restorePos = false;
7277             if(el.getStyle('position') == 'static'){
7278                 el.position('relative');
7279                 restorePos = true;
7280             }
7281             var x = 0, y =0;
7282             var op = this.dom;
7283             while(op && op != d && op.tagName != 'HTML'){
7284                 x+= op.offsetLeft;
7285                 y+= op.offsetTop;
7286                 op = op.offsetParent;
7287             }
7288             if(restorePos){
7289                 el.position('static');
7290             }
7291             return [x, y];
7292         },
7293
7294         /**
7295          * Scrolls this element into view within the passed container.
7296          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7297          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7298          * @return {Roo.Element} this
7299          */
7300         scrollIntoView : function(container, hscroll){
7301             var c = Roo.getDom(container) || document.body;
7302             var el = this.dom;
7303
7304             var o = this.calcOffsetsTo(c),
7305                 l = o[0],
7306                 t = o[1],
7307                 b = t+el.offsetHeight,
7308                 r = l+el.offsetWidth;
7309
7310             var ch = c.clientHeight;
7311             var ct = parseInt(c.scrollTop, 10);
7312             var cl = parseInt(c.scrollLeft, 10);
7313             var cb = ct + ch;
7314             var cr = cl + c.clientWidth;
7315
7316             if(t < ct){
7317                 c.scrollTop = t;
7318             }else if(b > cb){
7319                 c.scrollTop = b-ch;
7320             }
7321
7322             if(hscroll !== false){
7323                 if(l < cl){
7324                     c.scrollLeft = l;
7325                 }else if(r > cr){
7326                     c.scrollLeft = r-c.clientWidth;
7327                 }
7328             }
7329             return this;
7330         },
7331
7332         // private
7333         scrollChildIntoView : function(child, hscroll){
7334             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7335         },
7336
7337         /**
7338          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7339          * the new height may not be available immediately.
7340          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7341          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7342          * @param {Function} onComplete (optional) Function to call when animation completes
7343          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7344          * @return {Roo.Element} this
7345          */
7346         autoHeight : function(animate, duration, onComplete, easing){
7347             var oldHeight = this.getHeight();
7348             this.clip();
7349             this.setHeight(1); // force clipping
7350             setTimeout(function(){
7351                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7352                 if(!animate){
7353                     this.setHeight(height);
7354                     this.unclip();
7355                     if(typeof onComplete == "function"){
7356                         onComplete();
7357                     }
7358                 }else{
7359                     this.setHeight(oldHeight); // restore original height
7360                     this.setHeight(height, animate, duration, function(){
7361                         this.unclip();
7362                         if(typeof onComplete == "function") { onComplete(); }
7363                     }.createDelegate(this), easing);
7364                 }
7365             }.createDelegate(this), 0);
7366             return this;
7367         },
7368
7369         /**
7370          * Returns true if this element is an ancestor of the passed element
7371          * @param {HTMLElement/String} el The element to check
7372          * @return {Boolean} True if this element is an ancestor of el, else false
7373          */
7374         contains : function(el){
7375             if(!el){return false;}
7376             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7377         },
7378
7379         /**
7380          * Checks whether the element is currently visible using both visibility and display properties.
7381          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7382          * @return {Boolean} True if the element is currently visible, else false
7383          */
7384         isVisible : function(deep) {
7385             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7386             if(deep !== true || !vis){
7387                 return vis;
7388             }
7389             var p = this.dom.parentNode;
7390             while(p && p.tagName.toLowerCase() != "body"){
7391                 if(!Roo.fly(p, '_isVisible').isVisible()){
7392                     return false;
7393                 }
7394                 p = p.parentNode;
7395             }
7396             return true;
7397         },
7398
7399         /**
7400          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7401          * @param {String} selector The CSS selector
7402          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7403          * @return {CompositeElement/CompositeElementLite} The composite element
7404          */
7405         select : function(selector, unique){
7406             return El.select(selector, unique, this.dom);
7407         },
7408
7409         /**
7410          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7411          * @param {String} selector The CSS selector
7412          * @return {Array} An array of the matched nodes
7413          */
7414         query : function(selector, unique){
7415             return Roo.DomQuery.select(selector, this.dom);
7416         },
7417
7418         /**
7419          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7420          * @param {String} selector The CSS selector
7421          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7422          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7423          */
7424         child : function(selector, returnDom){
7425             var n = Roo.DomQuery.selectNode(selector, this.dom);
7426             return returnDom ? n : Roo.get(n);
7427         },
7428
7429         /**
7430          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7431          * @param {String} selector The CSS selector
7432          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7433          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7434          */
7435         down : function(selector, returnDom){
7436             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7437             return returnDom ? n : Roo.get(n);
7438         },
7439
7440         /**
7441          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7442          * @param {String} group The group the DD object is member of
7443          * @param {Object} config The DD config object
7444          * @param {Object} overrides An object containing methods to override/implement on the DD object
7445          * @return {Roo.dd.DD} The DD object
7446          */
7447         initDD : function(group, config, overrides){
7448             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7449             return Roo.apply(dd, overrides);
7450         },
7451
7452         /**
7453          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7454          * @param {String} group The group the DDProxy object is member of
7455          * @param {Object} config The DDProxy config object
7456          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7457          * @return {Roo.dd.DDProxy} The DDProxy object
7458          */
7459         initDDProxy : function(group, config, overrides){
7460             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7461             return Roo.apply(dd, overrides);
7462         },
7463
7464         /**
7465          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7466          * @param {String} group The group the DDTarget object is member of
7467          * @param {Object} config The DDTarget config object
7468          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7469          * @return {Roo.dd.DDTarget} The DDTarget object
7470          */
7471         initDDTarget : function(group, config, overrides){
7472             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7473             return Roo.apply(dd, overrides);
7474         },
7475
7476         /**
7477          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7478          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7479          * @param {Boolean} visible Whether the element is visible
7480          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7481          * @return {Roo.Element} this
7482          */
7483          setVisible : function(visible, animate){
7484             if(!animate || !A){
7485                 if(this.visibilityMode == El.DISPLAY){
7486                     this.setDisplayed(visible);
7487                 }else{
7488                     this.fixDisplay();
7489                     this.dom.style.visibility = visible ? "visible" : "hidden";
7490                 }
7491             }else{
7492                 // closure for composites
7493                 var dom = this.dom;
7494                 var visMode = this.visibilityMode;
7495                 if(visible){
7496                     this.setOpacity(.01);
7497                     this.setVisible(true);
7498                 }
7499                 this.anim({opacity: { to: (visible?1:0) }},
7500                       this.preanim(arguments, 1),
7501                       null, .35, 'easeIn', function(){
7502                          if(!visible){
7503                              if(visMode == El.DISPLAY){
7504                                  dom.style.display = "none";
7505                              }else{
7506                                  dom.style.visibility = "hidden";
7507                              }
7508                              Roo.get(dom).setOpacity(1);
7509                          }
7510                      });
7511             }
7512             return this;
7513         },
7514
7515         /**
7516          * Returns true if display is not "none"
7517          * @return {Boolean}
7518          */
7519         isDisplayed : function() {
7520             return this.getStyle("display") != "none";
7521         },
7522
7523         /**
7524          * Toggles the element's visibility or display, depending on visibility mode.
7525          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7526          * @return {Roo.Element} this
7527          */
7528         toggle : function(animate){
7529             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7530             return this;
7531         },
7532
7533         /**
7534          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7535          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7536          * @return {Roo.Element} this
7537          */
7538         setDisplayed : function(value) {
7539             if(typeof value == "boolean"){
7540                value = value ? this.originalDisplay : "none";
7541             }
7542             this.setStyle("display", value);
7543             return this;
7544         },
7545
7546         /**
7547          * Tries to focus the element. Any exceptions are caught and ignored.
7548          * @return {Roo.Element} this
7549          */
7550         focus : function() {
7551             try{
7552                 this.dom.focus();
7553             }catch(e){}
7554             return this;
7555         },
7556
7557         /**
7558          * Tries to blur the element. Any exceptions are caught and ignored.
7559          * @return {Roo.Element} this
7560          */
7561         blur : function() {
7562             try{
7563                 this.dom.blur();
7564             }catch(e){}
7565             return this;
7566         },
7567
7568         /**
7569          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7570          * @param {String/Array} className The CSS class to add, or an array of classes
7571          * @return {Roo.Element} this
7572          */
7573         addClass : function(className){
7574             if(className instanceof Array){
7575                 for(var i = 0, len = className.length; i < len; i++) {
7576                     this.addClass(className[i]);
7577                 }
7578             }else{
7579                 if(className && !this.hasClass(className)){
7580                     this.dom.className = this.dom.className + " " + className;
7581                 }
7582             }
7583             return this;
7584         },
7585
7586         /**
7587          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7588          * @param {String/Array} className The CSS class to add, or an array of classes
7589          * @return {Roo.Element} this
7590          */
7591         radioClass : function(className){
7592             var siblings = this.dom.parentNode.childNodes;
7593             for(var i = 0; i < siblings.length; i++) {
7594                 var s = siblings[i];
7595                 if(s.nodeType == 1){
7596                     Roo.get(s).removeClass(className);
7597                 }
7598             }
7599             this.addClass(className);
7600             return this;
7601         },
7602
7603         /**
7604          * Removes one or more CSS classes from the element.
7605          * @param {String/Array} className The CSS class to remove, or an array of classes
7606          * @return {Roo.Element} this
7607          */
7608         removeClass : function(className){
7609             if(!className || !this.dom.className){
7610                 return this;
7611             }
7612             if(className instanceof Array){
7613                 for(var i = 0, len = className.length; i < len; i++) {
7614                     this.removeClass(className[i]);
7615                 }
7616             }else{
7617                 if(this.hasClass(className)){
7618                     var re = this.classReCache[className];
7619                     if (!re) {
7620                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7621                        this.classReCache[className] = re;
7622                     }
7623                     this.dom.className =
7624                         this.dom.className.replace(re, " ");
7625                 }
7626             }
7627             return this;
7628         },
7629
7630         // private
7631         classReCache: {},
7632
7633         /**
7634          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7635          * @param {String} className The CSS class to toggle
7636          * @return {Roo.Element} this
7637          */
7638         toggleClass : function(className){
7639             if(this.hasClass(className)){
7640                 this.removeClass(className);
7641             }else{
7642                 this.addClass(className);
7643             }
7644             return this;
7645         },
7646
7647         /**
7648          * Checks if the specified CSS class exists on this element's DOM node.
7649          * @param {String} className The CSS class to check for
7650          * @return {Boolean} True if the class exists, else false
7651          */
7652         hasClass : function(className){
7653             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7654         },
7655
7656         /**
7657          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7658          * @param {String} oldClassName The CSS class to replace
7659          * @param {String} newClassName The replacement CSS class
7660          * @return {Roo.Element} this
7661          */
7662         replaceClass : function(oldClassName, newClassName){
7663             this.removeClass(oldClassName);
7664             this.addClass(newClassName);
7665             return this;
7666         },
7667
7668         /**
7669          * Returns an object with properties matching the styles requested.
7670          * For example, el.getStyles('color', 'font-size', 'width') might return
7671          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7672          * @param {String} style1 A style name
7673          * @param {String} style2 A style name
7674          * @param {String} etc.
7675          * @return {Object} The style object
7676          */
7677         getStyles : function(){
7678             var a = arguments, len = a.length, r = {};
7679             for(var i = 0; i < len; i++){
7680                 r[a[i]] = this.getStyle(a[i]);
7681             }
7682             return r;
7683         },
7684
7685         /**
7686          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7687          * @param {String} property The style property whose value is returned.
7688          * @return {String} The current value of the style property for this element.
7689          */
7690         getStyle : function(){
7691             return view && view.getComputedStyle ?
7692                 function(prop){
7693                     var el = this.dom, v, cs, camel;
7694                     if(prop == 'float'){
7695                         prop = "cssFloat";
7696                     }
7697                     if(el.style && (v = el.style[prop])){
7698                         return v;
7699                     }
7700                     if(cs = view.getComputedStyle(el, "")){
7701                         if(!(camel = propCache[prop])){
7702                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7703                         }
7704                         return cs[camel];
7705                     }
7706                     return null;
7707                 } :
7708                 function(prop){
7709                     var el = this.dom, v, cs, camel;
7710                     if(prop == 'opacity'){
7711                         if(typeof el.style.filter == 'string'){
7712                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7713                             if(m){
7714                                 var fv = parseFloat(m[1]);
7715                                 if(!isNaN(fv)){
7716                                     return fv ? fv / 100 : 0;
7717                                 }
7718                             }
7719                         }
7720                         return 1;
7721                     }else if(prop == 'float'){
7722                         prop = "styleFloat";
7723                     }
7724                     if(!(camel = propCache[prop])){
7725                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7726                     }
7727                     if(v = el.style[camel]){
7728                         return v;
7729                     }
7730                     if(cs = el.currentStyle){
7731                         return cs[camel];
7732                     }
7733                     return null;
7734                 };
7735         }(),
7736
7737         /**
7738          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7739          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7740          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7741          * @return {Roo.Element} this
7742          */
7743         setStyle : function(prop, value){
7744             if(typeof prop == "string"){
7745                 
7746                 if (prop == 'float') {
7747                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7748                     return this;
7749                 }
7750                 
7751                 var camel;
7752                 if(!(camel = propCache[prop])){
7753                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                 }
7755                 
7756                 if(camel == 'opacity') {
7757                     this.setOpacity(value);
7758                 }else{
7759                     this.dom.style[camel] = value;
7760                 }
7761             }else{
7762                 for(var style in prop){
7763                     if(typeof prop[style] != "function"){
7764                        this.setStyle(style, prop[style]);
7765                     }
7766                 }
7767             }
7768             return this;
7769         },
7770
7771         /**
7772          * More flexible version of {@link #setStyle} for setting style properties.
7773          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7774          * a function which returns such a specification.
7775          * @return {Roo.Element} this
7776          */
7777         applyStyles : function(style){
7778             Roo.DomHelper.applyStyles(this.dom, style);
7779             return this;
7780         },
7781
7782         /**
7783           * 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).
7784           * @return {Number} The X position of the element
7785           */
7786         getX : function(){
7787             return D.getX(this.dom);
7788         },
7789
7790         /**
7791           * 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).
7792           * @return {Number} The Y position of the element
7793           */
7794         getY : function(){
7795             return D.getY(this.dom);
7796         },
7797
7798         /**
7799           * 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).
7800           * @return {Array} The XY position of the element
7801           */
7802         getXY : function(){
7803             return D.getXY(this.dom);
7804         },
7805
7806         /**
7807          * 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).
7808          * @param {Number} The X position of the element
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         setX : function(x, animate){
7813             if(!animate || !A){
7814                 D.setX(this.dom, x);
7815             }else{
7816                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7817             }
7818             return this;
7819         },
7820
7821         /**
7822          * 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).
7823          * @param {Number} The Y position of the element
7824          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7825          * @return {Roo.Element} this
7826          */
7827         setY : function(y, animate){
7828             if(!animate || !A){
7829                 D.setY(this.dom, y);
7830             }else{
7831                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7832             }
7833             return this;
7834         },
7835
7836         /**
7837          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7838          * @param {String} left The left CSS property value
7839          * @return {Roo.Element} this
7840          */
7841         setLeft : function(left){
7842             this.setStyle("left", this.addUnits(left));
7843             return this;
7844         },
7845
7846         /**
7847          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7848          * @param {String} top The top CSS property value
7849          * @return {Roo.Element} this
7850          */
7851         setTop : function(top){
7852             this.setStyle("top", this.addUnits(top));
7853             return this;
7854         },
7855
7856         /**
7857          * Sets the element's CSS right style.
7858          * @param {String} right The right CSS property value
7859          * @return {Roo.Element} this
7860          */
7861         setRight : function(right){
7862             this.setStyle("right", this.addUnits(right));
7863             return this;
7864         },
7865
7866         /**
7867          * Sets the element's CSS bottom style.
7868          * @param {String} bottom The bottom CSS property value
7869          * @return {Roo.Element} this
7870          */
7871         setBottom : function(bottom){
7872             this.setStyle("bottom", this.addUnits(bottom));
7873             return this;
7874         },
7875
7876         /**
7877          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7878          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7879          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7880          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7881          * @return {Roo.Element} this
7882          */
7883         setXY : function(pos, animate){
7884             if(!animate || !A){
7885                 D.setXY(this.dom, pos);
7886             }else{
7887                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7888             }
7889             return this;
7890         },
7891
7892         /**
7893          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7894          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7895          * @param {Number} x X value for new position (coordinates are page-based)
7896          * @param {Number} y Y value for new position (coordinates are page-based)
7897          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7898          * @return {Roo.Element} this
7899          */
7900         setLocation : function(x, y, animate){
7901             this.setXY([x, y], this.preanim(arguments, 2));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7907          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7908          * @param {Number} x X value for new position (coordinates are page-based)
7909          * @param {Number} y Y value for new position (coordinates are page-based)
7910          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7911          * @return {Roo.Element} this
7912          */
7913         moveTo : function(x, y, animate){
7914             this.setXY([x, y], this.preanim(arguments, 2));
7915             return this;
7916         },
7917
7918         /**
7919          * Returns the region of the given element.
7920          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7921          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7922          */
7923         getRegion : function(){
7924             return D.getRegion(this.dom);
7925         },
7926
7927         /**
7928          * Returns the offset height of the element
7929          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7930          * @return {Number} The element's height
7931          */
7932         getHeight : function(contentHeight){
7933             var h = this.dom.offsetHeight || 0;
7934             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7935         },
7936
7937         /**
7938          * Returns the offset width of the element
7939          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7940          * @return {Number} The element's width
7941          */
7942         getWidth : function(contentWidth){
7943             var w = this.dom.offsetWidth || 0;
7944             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7945         },
7946
7947         /**
7948          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7949          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7950          * if a height has not been set using CSS.
7951          * @return {Number}
7952          */
7953         getComputedHeight : function(){
7954             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7955             if(!h){
7956                 h = parseInt(this.getStyle('height'), 10) || 0;
7957                 if(!this.isBorderBox()){
7958                     h += this.getFrameWidth('tb');
7959                 }
7960             }
7961             return h;
7962         },
7963
7964         /**
7965          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7966          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7967          * if a width has not been set using CSS.
7968          * @return {Number}
7969          */
7970         getComputedWidth : function(){
7971             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7972             if(!w){
7973                 w = parseInt(this.getStyle('width'), 10) || 0;
7974                 if(!this.isBorderBox()){
7975                     w += this.getFrameWidth('lr');
7976                 }
7977             }
7978             return w;
7979         },
7980
7981         /**
7982          * Returns the size of the element.
7983          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7984          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7985          */
7986         getSize : function(contentSize){
7987             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7988         },
7989
7990         /**
7991          * Returns the width and height of the viewport.
7992          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7993          */
7994         getViewSize : function(){
7995             var d = this.dom, doc = document, aw = 0, ah = 0;
7996             if(d == doc || d == doc.body){
7997                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7998             }else{
7999                 return {
8000                     width : d.clientWidth,
8001                     height: d.clientHeight
8002                 };
8003             }
8004         },
8005
8006         /**
8007          * Returns the value of the "value" attribute
8008          * @param {Boolean} asNumber true to parse the value as a number
8009          * @return {String/Number}
8010          */
8011         getValue : function(asNumber){
8012             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8013         },
8014
8015         // private
8016         adjustWidth : function(width){
8017             if(typeof width == "number"){
8018                 if(this.autoBoxAdjust && !this.isBorderBox()){
8019                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8020                 }
8021                 if(width < 0){
8022                     width = 0;
8023                 }
8024             }
8025             return width;
8026         },
8027
8028         // private
8029         adjustHeight : function(height){
8030             if(typeof height == "number"){
8031                if(this.autoBoxAdjust && !this.isBorderBox()){
8032                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8033                }
8034                if(height < 0){
8035                    height = 0;
8036                }
8037             }
8038             return height;
8039         },
8040
8041         /**
8042          * Set the width of the element
8043          * @param {Number} width The new width
8044          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8045          * @return {Roo.Element} this
8046          */
8047         setWidth : function(width, animate){
8048             width = this.adjustWidth(width);
8049             if(!animate || !A){
8050                 this.dom.style.width = this.addUnits(width);
8051             }else{
8052                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8053             }
8054             return this;
8055         },
8056
8057         /**
8058          * Set the height of the element
8059          * @param {Number} height The new height
8060          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8061          * @return {Roo.Element} this
8062          */
8063          setHeight : function(height, animate){
8064             height = this.adjustHeight(height);
8065             if(!animate || !A){
8066                 this.dom.style.height = this.addUnits(height);
8067             }else{
8068                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8069             }
8070             return this;
8071         },
8072
8073         /**
8074          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8075          * @param {Number} width The new width
8076          * @param {Number} height The new height
8077          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8078          * @return {Roo.Element} this
8079          */
8080          setSize : function(width, height, animate){
8081             if(typeof width == "object"){ // in case of object from getSize()
8082                 height = width.height; width = width.width;
8083             }
8084             width = this.adjustWidth(width); height = this.adjustHeight(height);
8085             if(!animate || !A){
8086                 this.dom.style.width = this.addUnits(width);
8087                 this.dom.style.height = this.addUnits(height);
8088             }else{
8089                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8090             }
8091             return this;
8092         },
8093
8094         /**
8095          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8096          * @param {Number} x X value for new position (coordinates are page-based)
8097          * @param {Number} y Y value for new position (coordinates are page-based)
8098          * @param {Number} width The new width
8099          * @param {Number} height The new height
8100          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8101          * @return {Roo.Element} this
8102          */
8103         setBounds : function(x, y, width, height, animate){
8104             if(!animate || !A){
8105                 this.setSize(width, height);
8106                 this.setLocation(x, y);
8107             }else{
8108                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8109                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8110                               this.preanim(arguments, 4), 'motion');
8111             }
8112             return this;
8113         },
8114
8115         /**
8116          * 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.
8117          * @param {Roo.lib.Region} region The region to fill
8118          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8119          * @return {Roo.Element} this
8120          */
8121         setRegion : function(region, animate){
8122             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8123             return this;
8124         },
8125
8126         /**
8127          * Appends an event handler
8128          *
8129          * @param {String}   eventName     The type of event to append
8130          * @param {Function} fn        The method the event invokes
8131          * @param {Object} scope       (optional) The scope (this object) of the fn
8132          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8133          */
8134         addListener : function(eventName, fn, scope, options){
8135             if (this.dom) {
8136                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8137             }
8138         },
8139
8140         /**
8141          * Removes an event handler from this element
8142          * @param {String} eventName the type of event to remove
8143          * @param {Function} fn the method the event invokes
8144          * @return {Roo.Element} this
8145          */
8146         removeListener : function(eventName, fn){
8147             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8148             return this;
8149         },
8150
8151         /**
8152          * Removes all previous added listeners from this element
8153          * @return {Roo.Element} this
8154          */
8155         removeAllListeners : function(){
8156             E.purgeElement(this.dom);
8157             return this;
8158         },
8159
8160         relayEvent : function(eventName, observable){
8161             this.on(eventName, function(e){
8162                 observable.fireEvent(eventName, e);
8163             });
8164         },
8165
8166         /**
8167          * Set the opacity of the element
8168          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
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          setOpacity : function(opacity, animate){
8173             if(!animate || !A){
8174                 var s = this.dom.style;
8175                 if(Roo.isIE){
8176                     s.zoom = 1;
8177                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8178                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8179                 }else{
8180                     s.opacity = opacity;
8181                 }
8182             }else{
8183                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8184             }
8185             return this;
8186         },
8187
8188         /**
8189          * Gets the left X coordinate
8190          * @param {Boolean} local True to get the local css position instead of page coordinate
8191          * @return {Number}
8192          */
8193         getLeft : function(local){
8194             if(!local){
8195                 return this.getX();
8196             }else{
8197                 return parseInt(this.getStyle("left"), 10) || 0;
8198             }
8199         },
8200
8201         /**
8202          * Gets the right X coordinate of the element (element X position + element width)
8203          * @param {Boolean} local True to get the local css position instead of page coordinate
8204          * @return {Number}
8205          */
8206         getRight : function(local){
8207             if(!local){
8208                 return this.getX() + this.getWidth();
8209             }else{
8210                 return (this.getLeft(true) + this.getWidth()) || 0;
8211             }
8212         },
8213
8214         /**
8215          * Gets the top Y coordinate
8216          * @param {Boolean} local True to get the local css position instead of page coordinate
8217          * @return {Number}
8218          */
8219         getTop : function(local) {
8220             if(!local){
8221                 return this.getY();
8222             }else{
8223                 return parseInt(this.getStyle("top"), 10) || 0;
8224             }
8225         },
8226
8227         /**
8228          * Gets the bottom Y coordinate of the element (element Y position + element height)
8229          * @param {Boolean} local True to get the local css position instead of page coordinate
8230          * @return {Number}
8231          */
8232         getBottom : function(local){
8233             if(!local){
8234                 return this.getY() + this.getHeight();
8235             }else{
8236                 return (this.getTop(true) + this.getHeight()) || 0;
8237             }
8238         },
8239
8240         /**
8241         * Initializes positioning on this element. If a desired position is not passed, it will make the
8242         * the element positioned relative IF it is not already positioned.
8243         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8244         * @param {Number} zIndex (optional) The zIndex to apply
8245         * @param {Number} x (optional) Set the page X position
8246         * @param {Number} y (optional) Set the page Y position
8247         */
8248         position : function(pos, zIndex, x, y){
8249             if(!pos){
8250                if(this.getStyle('position') == 'static'){
8251                    this.setStyle('position', 'relative');
8252                }
8253             }else{
8254                 this.setStyle("position", pos);
8255             }
8256             if(zIndex){
8257                 this.setStyle("z-index", zIndex);
8258             }
8259             if(x !== undefined && y !== undefined){
8260                 this.setXY([x, y]);
8261             }else if(x !== undefined){
8262                 this.setX(x);
8263             }else if(y !== undefined){
8264                 this.setY(y);
8265             }
8266         },
8267
8268         /**
8269         * Clear positioning back to the default when the document was loaded
8270         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8271         * @return {Roo.Element} this
8272          */
8273         clearPositioning : function(value){
8274             value = value ||'';
8275             this.setStyle({
8276                 "left": value,
8277                 "right": value,
8278                 "top": value,
8279                 "bottom": value,
8280                 "z-index": "",
8281                 "position" : "static"
8282             });
8283             return this;
8284         },
8285
8286         /**
8287         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8288         * snapshot before performing an update and then restoring the element.
8289         * @return {Object}
8290         */
8291         getPositioning : function(){
8292             var l = this.getStyle("left");
8293             var t = this.getStyle("top");
8294             return {
8295                 "position" : this.getStyle("position"),
8296                 "left" : l,
8297                 "right" : l ? "" : this.getStyle("right"),
8298                 "top" : t,
8299                 "bottom" : t ? "" : this.getStyle("bottom"),
8300                 "z-index" : this.getStyle("z-index")
8301             };
8302         },
8303
8304         /**
8305          * Gets the width of the border(s) for the specified side(s)
8306          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8307          * passing lr would get the border (l)eft width + the border (r)ight width.
8308          * @return {Number} The width of the sides passed added together
8309          */
8310         getBorderWidth : function(side){
8311             return this.addStyles(side, El.borders);
8312         },
8313
8314         /**
8315          * Gets the width of the padding(s) for the specified side(s)
8316          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8317          * passing lr would get the padding (l)eft + the padding (r)ight.
8318          * @return {Number} The padding of the sides passed added together
8319          */
8320         getPadding : function(side){
8321             return this.addStyles(side, El.paddings);
8322         },
8323
8324         /**
8325         * Set positioning with an object returned by getPositioning().
8326         * @param {Object} posCfg
8327         * @return {Roo.Element} this
8328          */
8329         setPositioning : function(pc){
8330             this.applyStyles(pc);
8331             if(pc.right == "auto"){
8332                 this.dom.style.right = "";
8333             }
8334             if(pc.bottom == "auto"){
8335                 this.dom.style.bottom = "";
8336             }
8337             return this;
8338         },
8339
8340         // private
8341         fixDisplay : function(){
8342             if(this.getStyle("display") == "none"){
8343                 this.setStyle("visibility", "hidden");
8344                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8345                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8346                     this.setStyle("display", "block");
8347                 }
8348             }
8349         },
8350
8351         /**
8352          * Quick set left and top adding default units
8353          * @param {String} left The left CSS property value
8354          * @param {String} top The top CSS property value
8355          * @return {Roo.Element} this
8356          */
8357          setLeftTop : function(left, top){
8358             this.dom.style.left = this.addUnits(left);
8359             this.dom.style.top = this.addUnits(top);
8360             return this;
8361         },
8362
8363         /**
8364          * Move this element relative to its current position.
8365          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8366          * @param {Number} distance How far to move the element in pixels
8367          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8368          * @return {Roo.Element} this
8369          */
8370          move : function(direction, distance, animate){
8371             var xy = this.getXY();
8372             direction = direction.toLowerCase();
8373             switch(direction){
8374                 case "l":
8375                 case "left":
8376                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8377                     break;
8378                case "r":
8379                case "right":
8380                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8381                     break;
8382                case "t":
8383                case "top":
8384                case "up":
8385                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8386                     break;
8387                case "b":
8388                case "bottom":
8389                case "down":
8390                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8391                     break;
8392             }
8393             return this;
8394         },
8395
8396         /**
8397          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8398          * @return {Roo.Element} this
8399          */
8400         clip : function(){
8401             if(!this.isClipped){
8402                this.isClipped = true;
8403                this.originalClip = {
8404                    "o": this.getStyle("overflow"),
8405                    "x": this.getStyle("overflow-x"),
8406                    "y": this.getStyle("overflow-y")
8407                };
8408                this.setStyle("overflow", "hidden");
8409                this.setStyle("overflow-x", "hidden");
8410                this.setStyle("overflow-y", "hidden");
8411             }
8412             return this;
8413         },
8414
8415         /**
8416          *  Return clipping (overflow) to original clipping before clip() was called
8417          * @return {Roo.Element} this
8418          */
8419         unclip : function(){
8420             if(this.isClipped){
8421                 this.isClipped = false;
8422                 var o = this.originalClip;
8423                 if(o.o){this.setStyle("overflow", o.o);}
8424                 if(o.x){this.setStyle("overflow-x", o.x);}
8425                 if(o.y){this.setStyle("overflow-y", o.y);}
8426             }
8427             return this;
8428         },
8429
8430
8431         /**
8432          * Gets the x,y coordinates specified by the anchor position on the element.
8433          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8434          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8435          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8436          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8437          * @return {Array} [x, y] An array containing the element's x and y coordinates
8438          */
8439         getAnchorXY : function(anchor, local, s){
8440             //Passing a different size is useful for pre-calculating anchors,
8441             //especially for anchored animations that change the el size.
8442
8443             var w, h, vp = false;
8444             if(!s){
8445                 var d = this.dom;
8446                 if(d == document.body || d == document){
8447                     vp = true;
8448                     w = D.getViewWidth(); h = D.getViewHeight();
8449                 }else{
8450                     w = this.getWidth(); h = this.getHeight();
8451                 }
8452             }else{
8453                 w = s.width;  h = s.height;
8454             }
8455             var x = 0, y = 0, r = Math.round;
8456             switch((anchor || "tl").toLowerCase()){
8457                 case "c":
8458                     x = r(w*.5);
8459                     y = r(h*.5);
8460                 break;
8461                 case "t":
8462                     x = r(w*.5);
8463                     y = 0;
8464                 break;
8465                 case "l":
8466                     x = 0;
8467                     y = r(h*.5);
8468                 break;
8469                 case "r":
8470                     x = w;
8471                     y = r(h*.5);
8472                 break;
8473                 case "b":
8474                     x = r(w*.5);
8475                     y = h;
8476                 break;
8477                 case "tl":
8478                     x = 0;
8479                     y = 0;
8480                 break;
8481                 case "bl":
8482                     x = 0;
8483                     y = h;
8484                 break;
8485                 case "br":
8486                     x = w;
8487                     y = h;
8488                 break;
8489                 case "tr":
8490                     x = w;
8491                     y = 0;
8492                 break;
8493             }
8494             if(local === true){
8495                 return [x, y];
8496             }
8497             if(vp){
8498                 var sc = this.getScroll();
8499                 return [x + sc.left, y + sc.top];
8500             }
8501             //Add the element's offset xy
8502             var o = this.getXY();
8503             return [x+o[0], y+o[1]];
8504         },
8505
8506         /**
8507          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8508          * supported position values.
8509          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8510          * @param {String} position The position to align to.
8511          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8512          * @return {Array} [x, y]
8513          */
8514         getAlignToXY : function(el, p, o){
8515             el = Roo.get(el);
8516             var d = this.dom;
8517             if(!el.dom){
8518                 throw "Element.alignTo with an element that doesn't exist";
8519             }
8520             var c = false; //constrain to viewport
8521             var p1 = "", p2 = "";
8522             o = o || [0,0];
8523
8524             if(!p){
8525                 p = "tl-bl";
8526             }else if(p == "?"){
8527                 p = "tl-bl?";
8528             }else if(p.indexOf("-") == -1){
8529                 p = "tl-" + p;
8530             }
8531             p = p.toLowerCase();
8532             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8533             if(!m){
8534                throw "Element.alignTo with an invalid alignment " + p;
8535             }
8536             p1 = m[1]; p2 = m[2]; c = !!m[3];
8537
8538             //Subtract the aligned el's internal xy from the target's offset xy
8539             //plus custom offset to get the aligned el's new offset xy
8540             var a1 = this.getAnchorXY(p1, true);
8541             var a2 = el.getAnchorXY(p2, false);
8542             var x = a2[0] - a1[0] + o[0];
8543             var y = a2[1] - a1[1] + o[1];
8544             if(c){
8545                 //constrain the aligned el to viewport if necessary
8546                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8547                 // 5px of margin for ie
8548                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8549
8550                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8551                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8552                 //otherwise swap the aligned el to the opposite border of the target.
8553                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8554                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8555                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8556                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8557
8558                var doc = document;
8559                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8560                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8561
8562                if((x+w) > dw + scrollX){
8563                     x = swapX ? r.left-w : dw+scrollX-w;
8564                 }
8565                if(x < scrollX){
8566                    x = swapX ? r.right : scrollX;
8567                }
8568                if((y+h) > dh + scrollY){
8569                     y = swapY ? r.top-h : dh+scrollY-h;
8570                 }
8571                if (y < scrollY){
8572                    y = swapY ? r.bottom : scrollY;
8573                }
8574             }
8575             return [x,y];
8576         },
8577
8578         // private
8579         getConstrainToXY : function(){
8580             var os = {top:0, left:0, bottom:0, right: 0};
8581
8582             return function(el, local, offsets, proposedXY){
8583                 el = Roo.get(el);
8584                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8585
8586                 var vw, vh, vx = 0, vy = 0;
8587                 if(el.dom == document.body || el.dom == document){
8588                     vw = Roo.lib.Dom.getViewWidth();
8589                     vh = Roo.lib.Dom.getViewHeight();
8590                 }else{
8591                     vw = el.dom.clientWidth;
8592                     vh = el.dom.clientHeight;
8593                     if(!local){
8594                         var vxy = el.getXY();
8595                         vx = vxy[0];
8596                         vy = vxy[1];
8597                     }
8598                 }
8599
8600                 var s = el.getScroll();
8601
8602                 vx += offsets.left + s.left;
8603                 vy += offsets.top + s.top;
8604
8605                 vw -= offsets.right;
8606                 vh -= offsets.bottom;
8607
8608                 var vr = vx+vw;
8609                 var vb = vy+vh;
8610
8611                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8612                 var x = xy[0], y = xy[1];
8613                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8614
8615                 // only move it if it needs it
8616                 var moved = false;
8617
8618                 // first validate right/bottom
8619                 if((x + w) > vr){
8620                     x = vr - w;
8621                     moved = true;
8622                 }
8623                 if((y + h) > vb){
8624                     y = vb - h;
8625                     moved = true;
8626                 }
8627                 // then make sure top/left isn't negative
8628                 if(x < vx){
8629                     x = vx;
8630                     moved = true;
8631                 }
8632                 if(y < vy){
8633                     y = vy;
8634                     moved = true;
8635                 }
8636                 return moved ? [x, y] : false;
8637             };
8638         }(),
8639
8640         // private
8641         adjustForConstraints : function(xy, parent, offsets){
8642             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8643         },
8644
8645         /**
8646          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8647          * document it aligns it to the viewport.
8648          * The position parameter is optional, and can be specified in any one of the following formats:
8649          * <ul>
8650          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8651          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8652          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8653          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8654          *   <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
8655          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8656          * </ul>
8657          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8658          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8659          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8660          * that specified in order to enforce the viewport constraints.
8661          * Following are all of the supported anchor positions:
8662     <pre>
8663     Value  Description
8664     -----  -----------------------------
8665     tl     The top left corner (default)
8666     t      The center of the top edge
8667     tr     The top right corner
8668     l      The center of the left edge
8669     c      In the center of the element
8670     r      The center of the right edge
8671     bl     The bottom left corner
8672     b      The center of the bottom edge
8673     br     The bottom right corner
8674     </pre>
8675     Example Usage:
8676     <pre><code>
8677     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8678     el.alignTo("other-el");
8679
8680     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8681     el.alignTo("other-el", "tr?");
8682
8683     // align the bottom right corner of el with the center left edge of other-el
8684     el.alignTo("other-el", "br-l?");
8685
8686     // align the center of el with the bottom left corner of other-el and
8687     // adjust the x position by -6 pixels (and the y position by 0)
8688     el.alignTo("other-el", "c-bl", [-6, 0]);
8689     </code></pre>
8690          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8691          * @param {String} position The position to align to.
8692          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8693          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8694          * @return {Roo.Element} this
8695          */
8696         alignTo : function(element, position, offsets, animate){
8697             var xy = this.getAlignToXY(element, position, offsets);
8698             this.setXY(xy, this.preanim(arguments, 3));
8699             return this;
8700         },
8701
8702         /**
8703          * Anchors an element to another element and realigns it when the window is resized.
8704          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8705          * @param {String} position The position to align to.
8706          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8707          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8708          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8709          * is a number, it is used as the buffer delay (defaults to 50ms).
8710          * @param {Function} callback The function to call after the animation finishes
8711          * @return {Roo.Element} this
8712          */
8713         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8714             var action = function(){
8715                 this.alignTo(el, alignment, offsets, animate);
8716                 Roo.callback(callback, this);
8717             };
8718             Roo.EventManager.onWindowResize(action, this);
8719             var tm = typeof monitorScroll;
8720             if(tm != 'undefined'){
8721                 Roo.EventManager.on(window, 'scroll', action, this,
8722                     {buffer: tm == 'number' ? monitorScroll : 50});
8723             }
8724             action.call(this); // align immediately
8725             return this;
8726         },
8727         /**
8728          * Clears any opacity settings from this element. Required in some cases for IE.
8729          * @return {Roo.Element} this
8730          */
8731         clearOpacity : function(){
8732             if (window.ActiveXObject) {
8733                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8734                     this.dom.style.filter = "";
8735                 }
8736             } else {
8737                 this.dom.style.opacity = "";
8738                 this.dom.style["-moz-opacity"] = "";
8739                 this.dom.style["-khtml-opacity"] = "";
8740             }
8741             return this;
8742         },
8743
8744         /**
8745          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8746          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8747          * @return {Roo.Element} this
8748          */
8749         hide : function(animate){
8750             this.setVisible(false, this.preanim(arguments, 0));
8751             return this;
8752         },
8753
8754         /**
8755         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8756         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8757          * @return {Roo.Element} this
8758          */
8759         show : function(animate){
8760             this.setVisible(true, this.preanim(arguments, 0));
8761             return this;
8762         },
8763
8764         /**
8765          * @private Test if size has a unit, otherwise appends the default
8766          */
8767         addUnits : function(size){
8768             return Roo.Element.addUnits(size, this.defaultUnit);
8769         },
8770
8771         /**
8772          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8773          * @return {Roo.Element} this
8774          */
8775         beginMeasure : function(){
8776             var el = this.dom;
8777             if(el.offsetWidth || el.offsetHeight){
8778                 return this; // offsets work already
8779             }
8780             var changed = [];
8781             var p = this.dom, b = document.body; // start with this element
8782             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8783                 var pe = Roo.get(p);
8784                 if(pe.getStyle('display') == 'none'){
8785                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8786                     p.style.visibility = "hidden";
8787                     p.style.display = "block";
8788                 }
8789                 p = p.parentNode;
8790             }
8791             this._measureChanged = changed;
8792             return this;
8793
8794         },
8795
8796         /**
8797          * Restores displays to before beginMeasure was called
8798          * @return {Roo.Element} this
8799          */
8800         endMeasure : function(){
8801             var changed = this._measureChanged;
8802             if(changed){
8803                 for(var i = 0, len = changed.length; i < len; i++) {
8804                     var r = changed[i];
8805                     r.el.style.visibility = r.visibility;
8806                     r.el.style.display = "none";
8807                 }
8808                 this._measureChanged = null;
8809             }
8810             return this;
8811         },
8812
8813         /**
8814         * Update the innerHTML of this element, optionally searching for and processing scripts
8815         * @param {String} html The new HTML
8816         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8817         * @param {Function} callback For async script loading you can be noticed when the update completes
8818         * @return {Roo.Element} this
8819          */
8820         update : function(html, loadScripts, callback){
8821             if(typeof html == "undefined"){
8822                 html = "";
8823             }
8824             if(loadScripts !== true){
8825                 this.dom.innerHTML = html;
8826                 if(typeof callback == "function"){
8827                     callback();
8828                 }
8829                 return this;
8830             }
8831             var id = Roo.id();
8832             var dom = this.dom;
8833
8834             html += '<span id="' + id + '"></span>';
8835
8836             E.onAvailable(id, function(){
8837                 var hd = document.getElementsByTagName("head")[0];
8838                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8839                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8840                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8841
8842                 var match;
8843                 while(match = re.exec(html)){
8844                     var attrs = match[1];
8845                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8846                     if(srcMatch && srcMatch[2]){
8847                        var s = document.createElement("script");
8848                        s.src = srcMatch[2];
8849                        var typeMatch = attrs.match(typeRe);
8850                        if(typeMatch && typeMatch[2]){
8851                            s.type = typeMatch[2];
8852                        }
8853                        hd.appendChild(s);
8854                     }else if(match[2] && match[2].length > 0){
8855                         if(window.execScript) {
8856                            window.execScript(match[2]);
8857                         } else {
8858                             /**
8859                              * eval:var:id
8860                              * eval:var:dom
8861                              * eval:var:html
8862                              * 
8863                              */
8864                            window.eval(match[2]);
8865                         }
8866                     }
8867                 }
8868                 var el = document.getElementById(id);
8869                 if(el){el.parentNode.removeChild(el);}
8870                 if(typeof callback == "function"){
8871                     callback();
8872                 }
8873             });
8874             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8875             return this;
8876         },
8877
8878         /**
8879          * Direct access to the UpdateManager update() method (takes the same parameters).
8880          * @param {String/Function} url The url for this request or a function to call to get the url
8881          * @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}
8882          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8883          * @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.
8884          * @return {Roo.Element} this
8885          */
8886         load : function(){
8887             var um = this.getUpdateManager();
8888             um.update.apply(um, arguments);
8889             return this;
8890         },
8891
8892         /**
8893         * Gets this element's UpdateManager
8894         * @return {Roo.UpdateManager} The UpdateManager
8895         */
8896         getUpdateManager : function(){
8897             if(!this.updateManager){
8898                 this.updateManager = new Roo.UpdateManager(this);
8899             }
8900             return this.updateManager;
8901         },
8902
8903         /**
8904          * Disables text selection for this element (normalized across browsers)
8905          * @return {Roo.Element} this
8906          */
8907         unselectable : function(){
8908             this.dom.unselectable = "on";
8909             this.swallowEvent("selectstart", true);
8910             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8911             this.addClass("x-unselectable");
8912             return this;
8913         },
8914
8915         /**
8916         * Calculates the x, y to center this element on the screen
8917         * @return {Array} The x, y values [x, y]
8918         */
8919         getCenterXY : function(){
8920             return this.getAlignToXY(document, 'c-c');
8921         },
8922
8923         /**
8924         * Centers the Element in either the viewport, or another Element.
8925         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8926         */
8927         center : function(centerIn){
8928             this.alignTo(centerIn || document, 'c-c');
8929             return this;
8930         },
8931
8932         /**
8933          * Tests various css rules/browsers to determine if this element uses a border box
8934          * @return {Boolean}
8935          */
8936         isBorderBox : function(){
8937             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8938         },
8939
8940         /**
8941          * Return a box {x, y, width, height} that can be used to set another elements
8942          * size/location to match this element.
8943          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8944          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8945          * @return {Object} box An object in the format {x, y, width, height}
8946          */
8947         getBox : function(contentBox, local){
8948             var xy;
8949             if(!local){
8950                 xy = this.getXY();
8951             }else{
8952                 var left = parseInt(this.getStyle("left"), 10) || 0;
8953                 var top = parseInt(this.getStyle("top"), 10) || 0;
8954                 xy = [left, top];
8955             }
8956             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8957             if(!contentBox){
8958                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8959             }else{
8960                 var l = this.getBorderWidth("l")+this.getPadding("l");
8961                 var r = this.getBorderWidth("r")+this.getPadding("r");
8962                 var t = this.getBorderWidth("t")+this.getPadding("t");
8963                 var b = this.getBorderWidth("b")+this.getPadding("b");
8964                 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)};
8965             }
8966             bx.right = bx.x + bx.width;
8967             bx.bottom = bx.y + bx.height;
8968             return bx;
8969         },
8970
8971         /**
8972          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8973          for more information about the sides.
8974          * @param {String} sides
8975          * @return {Number}
8976          */
8977         getFrameWidth : function(sides, onlyContentBox){
8978             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8979         },
8980
8981         /**
8982          * 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.
8983          * @param {Object} box The box to fill {x, y, width, height}
8984          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8985          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8986          * @return {Roo.Element} this
8987          */
8988         setBox : function(box, adjust, animate){
8989             var w = box.width, h = box.height;
8990             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8991                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8992                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8993             }
8994             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8995             return this;
8996         },
8997
8998         /**
8999          * Forces the browser to repaint this element
9000          * @return {Roo.Element} this
9001          */
9002          repaint : function(){
9003             var dom = this.dom;
9004             this.addClass("x-repaint");
9005             setTimeout(function(){
9006                 Roo.get(dom).removeClass("x-repaint");
9007             }, 1);
9008             return this;
9009         },
9010
9011         /**
9012          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9013          * then it returns the calculated width of the sides (see getPadding)
9014          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9015          * @return {Object/Number}
9016          */
9017         getMargins : function(side){
9018             if(!side){
9019                 return {
9020                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9021                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9022                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9023                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9024                 };
9025             }else{
9026                 return this.addStyles(side, El.margins);
9027              }
9028         },
9029
9030         // private
9031         addStyles : function(sides, styles){
9032             var val = 0, v, w;
9033             for(var i = 0, len = sides.length; i < len; i++){
9034                 v = this.getStyle(styles[sides.charAt(i)]);
9035                 if(v){
9036                      w = parseInt(v, 10);
9037                      if(w){ val += w; }
9038                 }
9039             }
9040             return val;
9041         },
9042
9043         /**
9044          * Creates a proxy element of this element
9045          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9046          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9047          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9048          * @return {Roo.Element} The new proxy element
9049          */
9050         createProxy : function(config, renderTo, matchBox){
9051             if(renderTo){
9052                 renderTo = Roo.getDom(renderTo);
9053             }else{
9054                 renderTo = document.body;
9055             }
9056             config = typeof config == "object" ?
9057                 config : {tag : "div", cls: config};
9058             var proxy = Roo.DomHelper.append(renderTo, config, true);
9059             if(matchBox){
9060                proxy.setBox(this.getBox());
9061             }
9062             return proxy;
9063         },
9064
9065         /**
9066          * Puts a mask over this element to disable user interaction. Requires core.css.
9067          * This method can only be applied to elements which accept child nodes.
9068          * @param {String} msg (optional) A message to display in the mask
9069          * @param {String} msgCls (optional) A css class to apply to the msg element
9070          * @return {Element} The mask  element
9071          */
9072         mask : function(msg, msgCls)
9073         {
9074             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9075                 this.setStyle("position", "relative");
9076             }
9077             if(!this._mask){
9078                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9079             }
9080             this.addClass("x-masked");
9081             this._mask.setDisplayed(true);
9082             
9083             // we wander
9084             var z = 0;
9085             var dom = this.dom;
9086             while (dom && dom.style) {
9087                 if (!isNaN(parseInt(dom.style.zIndex))) {
9088                     z = Math.max(z, parseInt(dom.style.zIndex));
9089                 }
9090                 dom = dom.parentNode;
9091             }
9092             // if we are masking the body - then it hides everything..
9093             if (this.dom == document.body) {
9094                 z = 1000000;
9095                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9096                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9097             }
9098            
9099             if(typeof msg == 'string'){
9100                 if(!this._maskMsg){
9101                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9102                 }
9103                 var mm = this._maskMsg;
9104                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9105                 if (mm.dom.firstChild) { // weird IE issue?
9106                     mm.dom.firstChild.innerHTML = msg;
9107                 }
9108                 mm.setDisplayed(true);
9109                 mm.center(this);
9110                 mm.setStyle('z-index', z + 102);
9111             }
9112             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9113                 this._mask.setHeight(this.getHeight());
9114             }
9115             this._mask.setStyle('z-index', z + 100);
9116             
9117             return this._mask;
9118         },
9119
9120         /**
9121          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9122          * it is cached for reuse.
9123          */
9124         unmask : function(removeEl){
9125             if(this._mask){
9126                 if(removeEl === true){
9127                     this._mask.remove();
9128                     delete this._mask;
9129                     if(this._maskMsg){
9130                         this._maskMsg.remove();
9131                         delete this._maskMsg;
9132                     }
9133                 }else{
9134                     this._mask.setDisplayed(false);
9135                     if(this._maskMsg){
9136                         this._maskMsg.setDisplayed(false);
9137                     }
9138                 }
9139             }
9140             this.removeClass("x-masked");
9141         },
9142
9143         /**
9144          * Returns true if this element is masked
9145          * @return {Boolean}
9146          */
9147         isMasked : function(){
9148             return this._mask && this._mask.isVisible();
9149         },
9150
9151         /**
9152          * Creates an iframe shim for this element to keep selects and other windowed objects from
9153          * showing through.
9154          * @return {Roo.Element} The new shim element
9155          */
9156         createShim : function(){
9157             var el = document.createElement('iframe');
9158             el.frameBorder = 'no';
9159             el.className = 'roo-shim';
9160             if(Roo.isIE && Roo.isSecure){
9161                 el.src = Roo.SSL_SECURE_URL;
9162             }
9163             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9164             shim.autoBoxAdjust = false;
9165             return shim;
9166         },
9167
9168         /**
9169          * Removes this element from the DOM and deletes it from the cache
9170          */
9171         remove : function(){
9172             if(this.dom.parentNode){
9173                 this.dom.parentNode.removeChild(this.dom);
9174             }
9175             delete El.cache[this.dom.id];
9176         },
9177
9178         /**
9179          * Sets up event handlers to add and remove a css class when the mouse is over this element
9180          * @param {String} className
9181          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9182          * mouseout events for children elements
9183          * @return {Roo.Element} this
9184          */
9185         addClassOnOver : function(className, preventFlicker){
9186             this.on("mouseover", function(){
9187                 Roo.fly(this, '_internal').addClass(className);
9188             }, this.dom);
9189             var removeFn = function(e){
9190                 if(preventFlicker !== true || !e.within(this, true)){
9191                     Roo.fly(this, '_internal').removeClass(className);
9192                 }
9193             };
9194             this.on("mouseout", removeFn, this.dom);
9195             return this;
9196         },
9197
9198         /**
9199          * Sets up event handlers to add and remove a css class when this element has the focus
9200          * @param {String} className
9201          * @return {Roo.Element} this
9202          */
9203         addClassOnFocus : function(className){
9204             this.on("focus", function(){
9205                 Roo.fly(this, '_internal').addClass(className);
9206             }, this.dom);
9207             this.on("blur", function(){
9208                 Roo.fly(this, '_internal').removeClass(className);
9209             }, this.dom);
9210             return this;
9211         },
9212         /**
9213          * 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)
9214          * @param {String} className
9215          * @return {Roo.Element} this
9216          */
9217         addClassOnClick : function(className){
9218             var dom = this.dom;
9219             this.on("mousedown", function(){
9220                 Roo.fly(dom, '_internal').addClass(className);
9221                 var d = Roo.get(document);
9222                 var fn = function(){
9223                     Roo.fly(dom, '_internal').removeClass(className);
9224                     d.removeListener("mouseup", fn);
9225                 };
9226                 d.on("mouseup", fn);
9227             });
9228             return this;
9229         },
9230
9231         /**
9232          * Stops the specified event from bubbling and optionally prevents the default action
9233          * @param {String} eventName
9234          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9235          * @return {Roo.Element} this
9236          */
9237         swallowEvent : function(eventName, preventDefault){
9238             var fn = function(e){
9239                 e.stopPropagation();
9240                 if(preventDefault){
9241                     e.preventDefault();
9242                 }
9243             };
9244             if(eventName instanceof Array){
9245                 for(var i = 0, len = eventName.length; i < len; i++){
9246                      this.on(eventName[i], fn);
9247                 }
9248                 return this;
9249             }
9250             this.on(eventName, fn);
9251             return this;
9252         },
9253
9254         /**
9255          * @private
9256          */
9257       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9258
9259         /**
9260          * Sizes this element to its parent element's dimensions performing
9261          * neccessary box adjustments.
9262          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9263          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9264          * @return {Roo.Element} this
9265          */
9266         fitToParent : function(monitorResize, targetParent) {
9267           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9268           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9269           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9270             return;
9271           }
9272           var p = Roo.get(targetParent || this.dom.parentNode);
9273           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9274           if (monitorResize === true) {
9275             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9276             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9277           }
9278           return this;
9279         },
9280
9281         /**
9282          * Gets the next sibling, skipping text nodes
9283          * @return {HTMLElement} The next sibling or null
9284          */
9285         getNextSibling : function(){
9286             var n = this.dom.nextSibling;
9287             while(n && n.nodeType != 1){
9288                 n = n.nextSibling;
9289             }
9290             return n;
9291         },
9292
9293         /**
9294          * Gets the previous sibling, skipping text nodes
9295          * @return {HTMLElement} The previous sibling or null
9296          */
9297         getPrevSibling : function(){
9298             var n = this.dom.previousSibling;
9299             while(n && n.nodeType != 1){
9300                 n = n.previousSibling;
9301             }
9302             return n;
9303         },
9304
9305
9306         /**
9307          * Appends the passed element(s) to this element
9308          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9309          * @return {Roo.Element} this
9310          */
9311         appendChild: function(el){
9312             el = Roo.get(el);
9313             el.appendTo(this);
9314             return this;
9315         },
9316
9317         /**
9318          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9319          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9320          * automatically generated with the specified attributes.
9321          * @param {HTMLElement} insertBefore (optional) a child element of this element
9322          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9323          * @return {Roo.Element} The new child element
9324          */
9325         createChild: function(config, insertBefore, returnDom){
9326             config = config || {tag:'div'};
9327             if(insertBefore){
9328                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9329             }
9330             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9331         },
9332
9333         /**
9334          * Appends this element to the passed element
9335          * @param {String/HTMLElement/Element} el The new parent element
9336          * @return {Roo.Element} this
9337          */
9338         appendTo: function(el){
9339             el = Roo.getDom(el);
9340             el.appendChild(this.dom);
9341             return this;
9342         },
9343
9344         /**
9345          * Inserts this element before the passed element in the DOM
9346          * @param {String/HTMLElement/Element} el The element to insert before
9347          * @return {Roo.Element} this
9348          */
9349         insertBefore: function(el){
9350             el = Roo.getDom(el);
9351             el.parentNode.insertBefore(this.dom, el);
9352             return this;
9353         },
9354
9355         /**
9356          * Inserts this element after the passed element in the DOM
9357          * @param {String/HTMLElement/Element} el The element to insert after
9358          * @return {Roo.Element} this
9359          */
9360         insertAfter: function(el){
9361             el = Roo.getDom(el);
9362             el.parentNode.insertBefore(this.dom, el.nextSibling);
9363             return this;
9364         },
9365
9366         /**
9367          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9368          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9369          * @return {Roo.Element} The new child
9370          */
9371         insertFirst: function(el, returnDom){
9372             el = el || {};
9373             if(typeof el == 'object' && !el.nodeType){ // dh config
9374                 return this.createChild(el, this.dom.firstChild, returnDom);
9375             }else{
9376                 el = Roo.getDom(el);
9377                 this.dom.insertBefore(el, this.dom.firstChild);
9378                 return !returnDom ? Roo.get(el) : el;
9379             }
9380         },
9381
9382         /**
9383          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9384          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9385          * @param {String} where (optional) 'before' or 'after' defaults to before
9386          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9387          * @return {Roo.Element} the inserted Element
9388          */
9389         insertSibling: function(el, where, returnDom){
9390             where = where ? where.toLowerCase() : 'before';
9391             el = el || {};
9392             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9393
9394             if(typeof el == 'object' && !el.nodeType){ // dh config
9395                 if(where == 'after' && !this.dom.nextSibling){
9396                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9397                 }else{
9398                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9399                 }
9400
9401             }else{
9402                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9403                             where == 'before' ? this.dom : this.dom.nextSibling);
9404                 if(!returnDom){
9405                     rt = Roo.get(rt);
9406                 }
9407             }
9408             return rt;
9409         },
9410
9411         /**
9412          * Creates and wraps this element with another element
9413          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9414          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9415          * @return {HTMLElement/Element} The newly created wrapper element
9416          */
9417         wrap: function(config, returnDom){
9418             if(!config){
9419                 config = {tag: "div"};
9420             }
9421             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9422             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9423             return newEl;
9424         },
9425
9426         /**
9427          * Replaces the passed element with this element
9428          * @param {String/HTMLElement/Element} el The element to replace
9429          * @return {Roo.Element} this
9430          */
9431         replace: function(el){
9432             el = Roo.get(el);
9433             this.insertBefore(el);
9434             el.remove();
9435             return this;
9436         },
9437
9438         /**
9439          * Inserts an html fragment into this element
9440          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9441          * @param {String} html The HTML fragment
9442          * @param {Boolean} returnEl True to return an Roo.Element
9443          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9444          */
9445         insertHtml : function(where, html, returnEl){
9446             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9447             return returnEl ? Roo.get(el) : el;
9448         },
9449
9450         /**
9451          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9452          * @param {Object} o The object with the attributes
9453          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9454          * @return {Roo.Element} this
9455          */
9456         set : function(o, useSet){
9457             var el = this.dom;
9458             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9459             for(var attr in o){
9460                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9461                 if(attr=="cls"){
9462                     el.className = o["cls"];
9463                 }else{
9464                     if(useSet) {
9465                         el.setAttribute(attr, o[attr]);
9466                     } else {
9467                         el[attr] = o[attr];
9468                     }
9469                 }
9470             }
9471             if(o.style){
9472                 Roo.DomHelper.applyStyles(el, o.style);
9473             }
9474             return this;
9475         },
9476
9477         /**
9478          * Convenience method for constructing a KeyMap
9479          * @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:
9480          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9481          * @param {Function} fn The function to call
9482          * @param {Object} scope (optional) The scope of the function
9483          * @return {Roo.KeyMap} The KeyMap created
9484          */
9485         addKeyListener : function(key, fn, scope){
9486             var config;
9487             if(typeof key != "object" || key instanceof Array){
9488                 config = {
9489                     key: key,
9490                     fn: fn,
9491                     scope: scope
9492                 };
9493             }else{
9494                 config = {
9495                     key : key.key,
9496                     shift : key.shift,
9497                     ctrl : key.ctrl,
9498                     alt : key.alt,
9499                     fn: fn,
9500                     scope: scope
9501                 };
9502             }
9503             return new Roo.KeyMap(this, config);
9504         },
9505
9506         /**
9507          * Creates a KeyMap for this element
9508          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9509          * @return {Roo.KeyMap} The KeyMap created
9510          */
9511         addKeyMap : function(config){
9512             return new Roo.KeyMap(this, config);
9513         },
9514
9515         /**
9516          * Returns true if this element is scrollable.
9517          * @return {Boolean}
9518          */
9519          isScrollable : function(){
9520             var dom = this.dom;
9521             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9522         },
9523
9524         /**
9525          * 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().
9526          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9527          * @param {Number} value The new scroll value
9528          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9529          * @return {Element} this
9530          */
9531
9532         scrollTo : function(side, value, animate){
9533             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9534             if(!animate || !A){
9535                 this.dom[prop] = value;
9536             }else{
9537                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9538                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9539             }
9540             return this;
9541         },
9542
9543         /**
9544          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9545          * within this element's scrollable range.
9546          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9547          * @param {Number} distance How far to scroll the element in pixels
9548          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9549          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9550          * was scrolled as far as it could go.
9551          */
9552          scroll : function(direction, distance, animate){
9553              if(!this.isScrollable()){
9554                  return;
9555              }
9556              var el = this.dom;
9557              var l = el.scrollLeft, t = el.scrollTop;
9558              var w = el.scrollWidth, h = el.scrollHeight;
9559              var cw = el.clientWidth, ch = el.clientHeight;
9560              direction = direction.toLowerCase();
9561              var scrolled = false;
9562              var a = this.preanim(arguments, 2);
9563              switch(direction){
9564                  case "l":
9565                  case "left":
9566                      if(w - l > cw){
9567                          var v = Math.min(l + distance, w-cw);
9568                          this.scrollTo("left", v, a);
9569                          scrolled = true;
9570                      }
9571                      break;
9572                 case "r":
9573                 case "right":
9574                      if(l > 0){
9575                          var v = Math.max(l - distance, 0);
9576                          this.scrollTo("left", v, a);
9577                          scrolled = true;
9578                      }
9579                      break;
9580                 case "t":
9581                 case "top":
9582                 case "up":
9583                      if(t > 0){
9584                          var v = Math.max(t - distance, 0);
9585                          this.scrollTo("top", v, a);
9586                          scrolled = true;
9587                      }
9588                      break;
9589                 case "b":
9590                 case "bottom":
9591                 case "down":
9592                      if(h - t > ch){
9593                          var v = Math.min(t + distance, h-ch);
9594                          this.scrollTo("top", v, a);
9595                          scrolled = true;
9596                      }
9597                      break;
9598              }
9599              return scrolled;
9600         },
9601
9602         /**
9603          * Translates the passed page coordinates into left/top css values for this element
9604          * @param {Number/Array} x The page x or an array containing [x, y]
9605          * @param {Number} y The page y
9606          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9607          */
9608         translatePoints : function(x, y){
9609             if(typeof x == 'object' || x instanceof Array){
9610                 y = x[1]; x = x[0];
9611             }
9612             var p = this.getStyle('position');
9613             var o = this.getXY();
9614
9615             var l = parseInt(this.getStyle('left'), 10);
9616             var t = parseInt(this.getStyle('top'), 10);
9617
9618             if(isNaN(l)){
9619                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9620             }
9621             if(isNaN(t)){
9622                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9623             }
9624
9625             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9626         },
9627
9628         /**
9629          * Returns the current scroll position of the element.
9630          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9631          */
9632         getScroll : function(){
9633             var d = this.dom, doc = document;
9634             if(d == doc || d == doc.body){
9635                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9636                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9637                 return {left: l, top: t};
9638             }else{
9639                 return {left: d.scrollLeft, top: d.scrollTop};
9640             }
9641         },
9642
9643         /**
9644          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9645          * are convert to standard 6 digit hex color.
9646          * @param {String} attr The css attribute
9647          * @param {String} defaultValue The default value to use when a valid color isn't found
9648          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9649          * YUI color anims.
9650          */
9651         getColor : function(attr, defaultValue, prefix){
9652             var v = this.getStyle(attr);
9653             if(!v || v == "transparent" || v == "inherit") {
9654                 return defaultValue;
9655             }
9656             var color = typeof prefix == "undefined" ? "#" : prefix;
9657             if(v.substr(0, 4) == "rgb("){
9658                 var rvs = v.slice(4, v.length -1).split(",");
9659                 for(var i = 0; i < 3; i++){
9660                     var h = parseInt(rvs[i]).toString(16);
9661                     if(h < 16){
9662                         h = "0" + h;
9663                     }
9664                     color += h;
9665                 }
9666             } else {
9667                 if(v.substr(0, 1) == "#"){
9668                     if(v.length == 4) {
9669                         for(var i = 1; i < 4; i++){
9670                             var c = v.charAt(i);
9671                             color +=  c + c;
9672                         }
9673                     }else if(v.length == 7){
9674                         color += v.substr(1);
9675                     }
9676                 }
9677             }
9678             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9679         },
9680
9681         /**
9682          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9683          * gradient background, rounded corners and a 4-way shadow.
9684          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9685          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9686          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9687          * @return {Roo.Element} this
9688          */
9689         boxWrap : function(cls){
9690             cls = cls || 'x-box';
9691             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9692             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9693             return el;
9694         },
9695
9696         /**
9697          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9698          * @param {String} namespace The namespace in which to look for the attribute
9699          * @param {String} name The attribute name
9700          * @return {String} The attribute value
9701          */
9702         getAttributeNS : Roo.isIE ? function(ns, name){
9703             var d = this.dom;
9704             var type = typeof d[ns+":"+name];
9705             if(type != 'undefined' && type != 'unknown'){
9706                 return d[ns+":"+name];
9707             }
9708             return d[name];
9709         } : function(ns, name){
9710             var d = this.dom;
9711             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9712         },
9713         
9714         
9715         /**
9716          * Sets or Returns the value the dom attribute value
9717          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9718          * @param {String} value (optional) The value to set the attribute to
9719          * @return {String} The attribute value
9720          */
9721         attr : function(name){
9722             if (arguments.length > 1) {
9723                 this.dom.setAttribute(name, arguments[1]);
9724                 return arguments[1];
9725             }
9726             if (typeof(name) == 'object') {
9727                 for(var i in name) {
9728                     this.attr(i, name[i]);
9729                 }
9730                 return name;
9731             }
9732             
9733             
9734             if (!this.dom.hasAttribute(name)) {
9735                 return undefined;
9736             }
9737             return this.dom.getAttribute(name);
9738         }
9739         
9740         
9741         
9742     };
9743
9744     var ep = El.prototype;
9745
9746     /**
9747      * Appends an event handler (Shorthand for addListener)
9748      * @param {String}   eventName     The type of event to append
9749      * @param {Function} fn        The method the event invokes
9750      * @param {Object} scope       (optional) The scope (this object) of the fn
9751      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9752      * @method
9753      */
9754     ep.on = ep.addListener;
9755         // backwards compat
9756     ep.mon = ep.addListener;
9757
9758     /**
9759      * Removes an event handler from this element (shorthand for removeListener)
9760      * @param {String} eventName the type of event to remove
9761      * @param {Function} fn the method the event invokes
9762      * @return {Roo.Element} this
9763      * @method
9764      */
9765     ep.un = ep.removeListener;
9766
9767     /**
9768      * true to automatically adjust width and height settings for box-model issues (default to true)
9769      */
9770     ep.autoBoxAdjust = true;
9771
9772     // private
9773     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9774
9775     // private
9776     El.addUnits = function(v, defaultUnit){
9777         if(v === "" || v == "auto"){
9778             return v;
9779         }
9780         if(v === undefined){
9781             return '';
9782         }
9783         if(typeof v == "number" || !El.unitPattern.test(v)){
9784             return v + (defaultUnit || 'px');
9785         }
9786         return v;
9787     };
9788
9789     // special markup used throughout Roo when box wrapping elements
9790     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>';
9791     /**
9792      * Visibility mode constant - Use visibility to hide element
9793      * @static
9794      * @type Number
9795      */
9796     El.VISIBILITY = 1;
9797     /**
9798      * Visibility mode constant - Use display to hide element
9799      * @static
9800      * @type Number
9801      */
9802     El.DISPLAY = 2;
9803
9804     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9805     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9806     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9807
9808
9809
9810     /**
9811      * @private
9812      */
9813     El.cache = {};
9814
9815     var docEl;
9816
9817     /**
9818      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9819      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9820      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9821      * @return {Element} The Element object
9822      * @static
9823      */
9824     El.get = function(el){
9825         var ex, elm, id;
9826         if(!el){ return null; }
9827         if(typeof el == "string"){ // element id
9828             if(!(elm = document.getElementById(el))){
9829                 return null;
9830             }
9831             if(ex = El.cache[el]){
9832                 ex.dom = elm;
9833             }else{
9834                 ex = El.cache[el] = new El(elm);
9835             }
9836             return ex;
9837         }else if(el.tagName){ // dom element
9838             if(!(id = el.id)){
9839                 id = Roo.id(el);
9840             }
9841             if(ex = El.cache[id]){
9842                 ex.dom = el;
9843             }else{
9844                 ex = El.cache[id] = new El(el);
9845             }
9846             return ex;
9847         }else if(el instanceof El){
9848             if(el != docEl){
9849                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9850                                                               // catch case where it hasn't been appended
9851                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9852             }
9853             return el;
9854         }else if(el.isComposite){
9855             return el;
9856         }else if(el instanceof Array){
9857             return El.select(el);
9858         }else if(el == document){
9859             // create a bogus element object representing the document object
9860             if(!docEl){
9861                 var f = function(){};
9862                 f.prototype = El.prototype;
9863                 docEl = new f();
9864                 docEl.dom = document;
9865             }
9866             return docEl;
9867         }
9868         return null;
9869     };
9870
9871     // private
9872     El.uncache = function(el){
9873         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9874             if(a[i]){
9875                 delete El.cache[a[i].id || a[i]];
9876             }
9877         }
9878     };
9879
9880     // private
9881     // Garbage collection - uncache elements/purge listeners on orphaned elements
9882     // so we don't hold a reference and cause the browser to retain them
9883     El.garbageCollect = function(){
9884         if(!Roo.enableGarbageCollector){
9885             clearInterval(El.collectorThread);
9886             return;
9887         }
9888         for(var eid in El.cache){
9889             var el = El.cache[eid], d = el.dom;
9890             // -------------------------------------------------------
9891             // Determining what is garbage:
9892             // -------------------------------------------------------
9893             // !d
9894             // dom node is null, definitely garbage
9895             // -------------------------------------------------------
9896             // !d.parentNode
9897             // no parentNode == direct orphan, definitely garbage
9898             // -------------------------------------------------------
9899             // !d.offsetParent && !document.getElementById(eid)
9900             // display none elements have no offsetParent so we will
9901             // also try to look it up by it's id. However, check
9902             // offsetParent first so we don't do unneeded lookups.
9903             // This enables collection of elements that are not orphans
9904             // directly, but somewhere up the line they have an orphan
9905             // parent.
9906             // -------------------------------------------------------
9907             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9908                 delete El.cache[eid];
9909                 if(d && Roo.enableListenerCollection){
9910                     E.purgeElement(d);
9911                 }
9912             }
9913         }
9914     }
9915     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9916
9917
9918     // dom is optional
9919     El.Flyweight = function(dom){
9920         this.dom = dom;
9921     };
9922     El.Flyweight.prototype = El.prototype;
9923
9924     El._flyweights = {};
9925     /**
9926      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9927      * the dom node can be overwritten by other code.
9928      * @param {String/HTMLElement} el The dom node or id
9929      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9930      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9931      * @static
9932      * @return {Element} The shared Element object
9933      */
9934     El.fly = function(el, named){
9935         named = named || '_global';
9936         el = Roo.getDom(el);
9937         if(!el){
9938             return null;
9939         }
9940         if(!El._flyweights[named]){
9941             El._flyweights[named] = new El.Flyweight();
9942         }
9943         El._flyweights[named].dom = el;
9944         return El._flyweights[named];
9945     };
9946
9947     /**
9948      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9949      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9950      * Shorthand of {@link Roo.Element#get}
9951      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9952      * @return {Element} The Element object
9953      * @member Roo
9954      * @method get
9955      */
9956     Roo.get = El.get;
9957     /**
9958      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9959      * the dom node can be overwritten by other code.
9960      * Shorthand of {@link Roo.Element#fly}
9961      * @param {String/HTMLElement} el The dom node or id
9962      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9963      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9964      * @static
9965      * @return {Element} The shared Element object
9966      * @member Roo
9967      * @method fly
9968      */
9969     Roo.fly = El.fly;
9970
9971     // speedy lookup for elements never to box adjust
9972     var noBoxAdjust = Roo.isStrict ? {
9973         select:1
9974     } : {
9975         input:1, select:1, textarea:1
9976     };
9977     if(Roo.isIE || Roo.isGecko){
9978         noBoxAdjust['button'] = 1;
9979     }
9980
9981
9982     Roo.EventManager.on(window, 'unload', function(){
9983         delete El.cache;
9984         delete El._flyweights;
9985     });
9986 })();
9987
9988
9989
9990
9991 if(Roo.DomQuery){
9992     Roo.Element.selectorFunction = Roo.DomQuery.select;
9993 }
9994
9995 Roo.Element.select = function(selector, unique, root){
9996     var els;
9997     if(typeof selector == "string"){
9998         els = Roo.Element.selectorFunction(selector, root);
9999     }else if(selector.length !== undefined){
10000         els = selector;
10001     }else{
10002         throw "Invalid selector";
10003     }
10004     if(unique === true){
10005         return new Roo.CompositeElement(els);
10006     }else{
10007         return new Roo.CompositeElementLite(els);
10008     }
10009 };
10010 /**
10011  * Selects elements based on the passed CSS selector to enable working on them as 1.
10012  * @param {String/Array} selector The CSS selector or an array of elements
10013  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10014  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10015  * @return {CompositeElementLite/CompositeElement}
10016  * @member Roo
10017  * @method select
10018  */
10019 Roo.select = Roo.Element.select;
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034 /*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044
10045
10046
10047 //Notifies Element that fx methods are available
10048 Roo.enableFx = true;
10049
10050 /**
10051  * @class Roo.Fx
10052  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10053  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10054  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10055  * Element effects to work.</p><br/>
10056  *
10057  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10058  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10059  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10060  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10061  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10062  * expected results and should be done with care.</p><br/>
10063  *
10064  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10065  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10066 <pre>
10067 Value  Description
10068 -----  -----------------------------
10069 tl     The top left corner
10070 t      The center of the top edge
10071 tr     The top right corner
10072 l      The center of the left edge
10073 r      The center of the right edge
10074 bl     The bottom left corner
10075 b      The center of the bottom edge
10076 br     The bottom right corner
10077 </pre>
10078  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10079  * below are common options that can be passed to any Fx method.</b>
10080  * @cfg {Function} callback A function called when the effect is finished
10081  * @cfg {Object} scope The scope of the effect function
10082  * @cfg {String} easing A valid Easing value for the effect
10083  * @cfg {String} afterCls A css class to apply after the effect
10084  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10085  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10086  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10087  * effects that end with the element being visually hidden, ignored otherwise)
10088  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10089  * a function which returns such a specification that will be applied to the Element after the effect finishes
10090  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10091  * @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
10092  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10093  */
10094 Roo.Fx = {
10095         /**
10096          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10097          * origin for the slide effect.  This function automatically handles wrapping the element with
10098          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10099          * Usage:
10100          *<pre><code>
10101 // default: slide the element in from the top
10102 el.slideIn();
10103
10104 // custom: slide the element in from the right with a 2-second duration
10105 el.slideIn('r', { duration: 2 });
10106
10107 // common config options shown with default values
10108 el.slideIn('t', {
10109     easing: 'easeOut',
10110     duration: .5
10111 });
10112 </code></pre>
10113          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10114          * @param {Object} options (optional) Object literal with any of the Fx config options
10115          * @return {Roo.Element} The Element
10116          */
10117     slideIn : function(anchor, o){
10118         var el = this.getFxEl();
10119         o = o || {};
10120
10121         el.queueFx(o, function(){
10122
10123             anchor = anchor || "t";
10124
10125             // fix display to visibility
10126             this.fixDisplay();
10127
10128             // restore values after effect
10129             var r = this.getFxRestore();
10130             var b = this.getBox();
10131             // fixed size for slide
10132             this.setSize(b);
10133
10134             // wrap if needed
10135             var wrap = this.fxWrap(r.pos, o, "hidden");
10136
10137             var st = this.dom.style;
10138             st.visibility = "visible";
10139             st.position = "absolute";
10140
10141             // clear out temp styles after slide and unwrap
10142             var after = function(){
10143                 el.fxUnwrap(wrap, r.pos, o);
10144                 st.width = r.width;
10145                 st.height = r.height;
10146                 el.afterFx(o);
10147             };
10148             // time to calc the positions
10149             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10150
10151             switch(anchor.toLowerCase()){
10152                 case "t":
10153                     wrap.setSize(b.width, 0);
10154                     st.left = st.bottom = "0";
10155                     a = {height: bh};
10156                 break;
10157                 case "l":
10158                     wrap.setSize(0, b.height);
10159                     st.right = st.top = "0";
10160                     a = {width: bw};
10161                 break;
10162                 case "r":
10163                     wrap.setSize(0, b.height);
10164                     wrap.setX(b.right);
10165                     st.left = st.top = "0";
10166                     a = {width: bw, points: pt};
10167                 break;
10168                 case "b":
10169                     wrap.setSize(b.width, 0);
10170                     wrap.setY(b.bottom);
10171                     st.left = st.top = "0";
10172                     a = {height: bh, points: pt};
10173                 break;
10174                 case "tl":
10175                     wrap.setSize(0, 0);
10176                     st.right = st.bottom = "0";
10177                     a = {width: bw, height: bh};
10178                 break;
10179                 case "bl":
10180                     wrap.setSize(0, 0);
10181                     wrap.setY(b.y+b.height);
10182                     st.right = st.top = "0";
10183                     a = {width: bw, height: bh, points: pt};
10184                 break;
10185                 case "br":
10186                     wrap.setSize(0, 0);
10187                     wrap.setXY([b.right, b.bottom]);
10188                     st.left = st.top = "0";
10189                     a = {width: bw, height: bh, points: pt};
10190                 break;
10191                 case "tr":
10192                     wrap.setSize(0, 0);
10193                     wrap.setX(b.x+b.width);
10194                     st.left = st.bottom = "0";
10195                     a = {width: bw, height: bh, points: pt};
10196                 break;
10197             }
10198             this.dom.style.visibility = "visible";
10199             wrap.show();
10200
10201             arguments.callee.anim = wrap.fxanim(a,
10202                 o,
10203                 'motion',
10204                 .5,
10205                 'easeOut', after);
10206         });
10207         return this;
10208     },
10209     
10210         /**
10211          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10212          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10213          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10214          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10215          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10216          * Usage:
10217          *<pre><code>
10218 // default: slide the element out to the top
10219 el.slideOut();
10220
10221 // custom: slide the element out to the right with a 2-second duration
10222 el.slideOut('r', { duration: 2 });
10223
10224 // common config options shown with default values
10225 el.slideOut('t', {
10226     easing: 'easeOut',
10227     duration: .5,
10228     remove: false,
10229     useDisplay: false
10230 });
10231 </code></pre>
10232          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10233          * @param {Object} options (optional) Object literal with any of the Fx config options
10234          * @return {Roo.Element} The Element
10235          */
10236     slideOut : function(anchor, o){
10237         var el = this.getFxEl();
10238         o = o || {};
10239
10240         el.queueFx(o, function(){
10241
10242             anchor = anchor || "t";
10243
10244             // restore values after effect
10245             var r = this.getFxRestore();
10246             
10247             var b = this.getBox();
10248             // fixed size for slide
10249             this.setSize(b);
10250
10251             // wrap if needed
10252             var wrap = this.fxWrap(r.pos, o, "visible");
10253
10254             var st = this.dom.style;
10255             st.visibility = "visible";
10256             st.position = "absolute";
10257
10258             wrap.setSize(b);
10259
10260             var after = function(){
10261                 if(o.useDisplay){
10262                     el.setDisplayed(false);
10263                 }else{
10264                     el.hide();
10265                 }
10266
10267                 el.fxUnwrap(wrap, r.pos, o);
10268
10269                 st.width = r.width;
10270                 st.height = r.height;
10271
10272                 el.afterFx(o);
10273             };
10274
10275             var a, zero = {to: 0};
10276             switch(anchor.toLowerCase()){
10277                 case "t":
10278                     st.left = st.bottom = "0";
10279                     a = {height: zero};
10280                 break;
10281                 case "l":
10282                     st.right = st.top = "0";
10283                     a = {width: zero};
10284                 break;
10285                 case "r":
10286                     st.left = st.top = "0";
10287                     a = {width: zero, points: {to:[b.right, b.y]}};
10288                 break;
10289                 case "b":
10290                     st.left = st.top = "0";
10291                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10292                 break;
10293                 case "tl":
10294                     st.right = st.bottom = "0";
10295                     a = {width: zero, height: zero};
10296                 break;
10297                 case "bl":
10298                     st.right = st.top = "0";
10299                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10300                 break;
10301                 case "br":
10302                     st.left = st.top = "0";
10303                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10304                 break;
10305                 case "tr":
10306                     st.left = st.bottom = "0";
10307                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10308                 break;
10309             }
10310
10311             arguments.callee.anim = wrap.fxanim(a,
10312                 o,
10313                 'motion',
10314                 .5,
10315                 "easeOut", after);
10316         });
10317         return this;
10318     },
10319
10320         /**
10321          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10322          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10323          * The element must be removed from the DOM using the 'remove' config option if desired.
10324          * Usage:
10325          *<pre><code>
10326 // default
10327 el.puff();
10328
10329 // common config options shown with default values
10330 el.puff({
10331     easing: 'easeOut',
10332     duration: .5,
10333     remove: false,
10334     useDisplay: false
10335 });
10336 </code></pre>
10337          * @param {Object} options (optional) Object literal with any of the Fx config options
10338          * @return {Roo.Element} The Element
10339          */
10340     puff : function(o){
10341         var el = this.getFxEl();
10342         o = o || {};
10343
10344         el.queueFx(o, function(){
10345             this.clearOpacity();
10346             this.show();
10347
10348             // restore values after effect
10349             var r = this.getFxRestore();
10350             var st = this.dom.style;
10351
10352             var after = function(){
10353                 if(o.useDisplay){
10354                     el.setDisplayed(false);
10355                 }else{
10356                     el.hide();
10357                 }
10358
10359                 el.clearOpacity();
10360
10361                 el.setPositioning(r.pos);
10362                 st.width = r.width;
10363                 st.height = r.height;
10364                 st.fontSize = '';
10365                 el.afterFx(o);
10366             };
10367
10368             var width = this.getWidth();
10369             var height = this.getHeight();
10370
10371             arguments.callee.anim = this.fxanim({
10372                     width : {to: this.adjustWidth(width * 2)},
10373                     height : {to: this.adjustHeight(height * 2)},
10374                     points : {by: [-(width * .5), -(height * .5)]},
10375                     opacity : {to: 0},
10376                     fontSize: {to:200, unit: "%"}
10377                 },
10378                 o,
10379                 'motion',
10380                 .5,
10381                 "easeOut", after);
10382         });
10383         return this;
10384     },
10385
10386         /**
10387          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10388          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10389          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10390          * Usage:
10391          *<pre><code>
10392 // default
10393 el.switchOff();
10394
10395 // all config options shown with default values
10396 el.switchOff({
10397     easing: 'easeIn',
10398     duration: .3,
10399     remove: false,
10400     useDisplay: false
10401 });
10402 </code></pre>
10403          * @param {Object} options (optional) Object literal with any of the Fx config options
10404          * @return {Roo.Element} The Element
10405          */
10406     switchOff : function(o){
10407         var el = this.getFxEl();
10408         o = o || {};
10409
10410         el.queueFx(o, function(){
10411             this.clearOpacity();
10412             this.clip();
10413
10414             // restore values after effect
10415             var r = this.getFxRestore();
10416             var st = this.dom.style;
10417
10418             var after = function(){
10419                 if(o.useDisplay){
10420                     el.setDisplayed(false);
10421                 }else{
10422                     el.hide();
10423                 }
10424
10425                 el.clearOpacity();
10426                 el.setPositioning(r.pos);
10427                 st.width = r.width;
10428                 st.height = r.height;
10429
10430                 el.afterFx(o);
10431             };
10432
10433             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10434                 this.clearOpacity();
10435                 (function(){
10436                     this.fxanim({
10437                         height:{to:1},
10438                         points:{by:[0, this.getHeight() * .5]}
10439                     }, o, 'motion', 0.3, 'easeIn', after);
10440                 }).defer(100, this);
10441             });
10442         });
10443         return this;
10444     },
10445
10446     /**
10447      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10448      * changed using the "attr" config option) and then fading back to the original color. If no original
10449      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10450      * Usage:
10451 <pre><code>
10452 // default: highlight background to yellow
10453 el.highlight();
10454
10455 // custom: highlight foreground text to blue for 2 seconds
10456 el.highlight("0000ff", { attr: 'color', duration: 2 });
10457
10458 // common config options shown with default values
10459 el.highlight("ffff9c", {
10460     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10461     endColor: (current color) or "ffffff",
10462     easing: 'easeIn',
10463     duration: 1
10464 });
10465 </code></pre>
10466      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10467      * @param {Object} options (optional) Object literal with any of the Fx config options
10468      * @return {Roo.Element} The Element
10469      */ 
10470     highlight : function(color, o){
10471         var el = this.getFxEl();
10472         o = o || {};
10473
10474         el.queueFx(o, function(){
10475             color = color || "ffff9c";
10476             attr = o.attr || "backgroundColor";
10477
10478             this.clearOpacity();
10479             this.show();
10480
10481             var origColor = this.getColor(attr);
10482             var restoreColor = this.dom.style[attr];
10483             endColor = (o.endColor || origColor) || "ffffff";
10484
10485             var after = function(){
10486                 el.dom.style[attr] = restoreColor;
10487                 el.afterFx(o);
10488             };
10489
10490             var a = {};
10491             a[attr] = {from: color, to: endColor};
10492             arguments.callee.anim = this.fxanim(a,
10493                 o,
10494                 'color',
10495                 1,
10496                 'easeIn', after);
10497         });
10498         return this;
10499     },
10500
10501    /**
10502     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10503     * Usage:
10504 <pre><code>
10505 // default: a single light blue ripple
10506 el.frame();
10507
10508 // custom: 3 red ripples lasting 3 seconds total
10509 el.frame("ff0000", 3, { duration: 3 });
10510
10511 // common config options shown with default values
10512 el.frame("C3DAF9", 1, {
10513     duration: 1 //duration of entire animation (not each individual ripple)
10514     // Note: Easing is not configurable and will be ignored if included
10515 });
10516 </code></pre>
10517     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10518     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10519     * @param {Object} options (optional) Object literal with any of the Fx config options
10520     * @return {Roo.Element} The Element
10521     */
10522     frame : function(color, count, o){
10523         var el = this.getFxEl();
10524         o = o || {};
10525
10526         el.queueFx(o, function(){
10527             color = color || "#C3DAF9";
10528             if(color.length == 6){
10529                 color = "#" + color;
10530             }
10531             count = count || 1;
10532             duration = o.duration || 1;
10533             this.show();
10534
10535             var b = this.getBox();
10536             var animFn = function(){
10537                 var proxy = this.createProxy({
10538
10539                      style:{
10540                         visbility:"hidden",
10541                         position:"absolute",
10542                         "z-index":"35000", // yee haw
10543                         border:"0px solid " + color
10544                      }
10545                   });
10546                 var scale = Roo.isBorderBox ? 2 : 1;
10547                 proxy.animate({
10548                     top:{from:b.y, to:b.y - 20},
10549                     left:{from:b.x, to:b.x - 20},
10550                     borderWidth:{from:0, to:10},
10551                     opacity:{from:1, to:0},
10552                     height:{from:b.height, to:(b.height + (20*scale))},
10553                     width:{from:b.width, to:(b.width + (20*scale))}
10554                 }, duration, function(){
10555                     proxy.remove();
10556                 });
10557                 if(--count > 0){
10558                      animFn.defer((duration/2)*1000, this);
10559                 }else{
10560                     el.afterFx(o);
10561                 }
10562             };
10563             animFn.call(this);
10564         });
10565         return this;
10566     },
10567
10568    /**
10569     * Creates a pause before any subsequent queued effects begin.  If there are
10570     * no effects queued after the pause it will have no effect.
10571     * Usage:
10572 <pre><code>
10573 el.pause(1);
10574 </code></pre>
10575     * @param {Number} seconds The length of time to pause (in seconds)
10576     * @return {Roo.Element} The Element
10577     */
10578     pause : function(seconds){
10579         var el = this.getFxEl();
10580         var o = {};
10581
10582         el.queueFx(o, function(){
10583             setTimeout(function(){
10584                 el.afterFx(o);
10585             }, seconds * 1000);
10586         });
10587         return this;
10588     },
10589
10590    /**
10591     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10592     * using the "endOpacity" config option.
10593     * Usage:
10594 <pre><code>
10595 // default: fade in from opacity 0 to 100%
10596 el.fadeIn();
10597
10598 // custom: fade in from opacity 0 to 75% over 2 seconds
10599 el.fadeIn({ endOpacity: .75, duration: 2});
10600
10601 // common config options shown with default values
10602 el.fadeIn({
10603     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10604     easing: 'easeOut',
10605     duration: .5
10606 });
10607 </code></pre>
10608     * @param {Object} options (optional) Object literal with any of the Fx config options
10609     * @return {Roo.Element} The Element
10610     */
10611     fadeIn : function(o){
10612         var el = this.getFxEl();
10613         o = o || {};
10614         el.queueFx(o, function(){
10615             this.setOpacity(0);
10616             this.fixDisplay();
10617             this.dom.style.visibility = 'visible';
10618             var to = o.endOpacity || 1;
10619             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10620                 o, null, .5, "easeOut", function(){
10621                 if(to == 1){
10622                     this.clearOpacity();
10623                 }
10624                 el.afterFx(o);
10625             });
10626         });
10627         return this;
10628     },
10629
10630    /**
10631     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10632     * using the "endOpacity" config option.
10633     * Usage:
10634 <pre><code>
10635 // default: fade out from the element's current opacity to 0
10636 el.fadeOut();
10637
10638 // custom: fade out from the element's current opacity to 25% over 2 seconds
10639 el.fadeOut({ endOpacity: .25, duration: 2});
10640
10641 // common config options shown with default values
10642 el.fadeOut({
10643     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10644     easing: 'easeOut',
10645     duration: .5
10646     remove: false,
10647     useDisplay: false
10648 });
10649 </code></pre>
10650     * @param {Object} options (optional) Object literal with any of the Fx config options
10651     * @return {Roo.Element} The Element
10652     */
10653     fadeOut : function(o){
10654         var el = this.getFxEl();
10655         o = o || {};
10656         el.queueFx(o, function(){
10657             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10658                 o, null, .5, "easeOut", function(){
10659                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10660                      this.dom.style.display = "none";
10661                 }else{
10662                      this.dom.style.visibility = "hidden";
10663                 }
10664                 this.clearOpacity();
10665                 el.afterFx(o);
10666             });
10667         });
10668         return this;
10669     },
10670
10671    /**
10672     * Animates the transition of an element's dimensions from a starting height/width
10673     * to an ending height/width.
10674     * Usage:
10675 <pre><code>
10676 // change height and width to 100x100 pixels
10677 el.scale(100, 100);
10678
10679 // common config options shown with default values.  The height and width will default to
10680 // the element's existing values if passed as null.
10681 el.scale(
10682     [element's width],
10683     [element's height], {
10684     easing: 'easeOut',
10685     duration: .35
10686 });
10687 </code></pre>
10688     * @param {Number} width  The new width (pass undefined to keep the original width)
10689     * @param {Number} height  The new height (pass undefined to keep the original height)
10690     * @param {Object} options (optional) Object literal with any of the Fx config options
10691     * @return {Roo.Element} The Element
10692     */
10693     scale : function(w, h, o){
10694         this.shift(Roo.apply({}, o, {
10695             width: w,
10696             height: h
10697         }));
10698         return this;
10699     },
10700
10701    /**
10702     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10703     * Any of these properties not specified in the config object will not be changed.  This effect 
10704     * requires that at least one new dimension, position or opacity setting must be passed in on
10705     * the config object in order for the function to have any effect.
10706     * Usage:
10707 <pre><code>
10708 // slide the element horizontally to x position 200 while changing the height and opacity
10709 el.shift({ x: 200, height: 50, opacity: .8 });
10710
10711 // common config options shown with default values.
10712 el.shift({
10713     width: [element's width],
10714     height: [element's height],
10715     x: [element's x position],
10716     y: [element's y position],
10717     opacity: [element's opacity],
10718     easing: 'easeOut',
10719     duration: .35
10720 });
10721 </code></pre>
10722     * @param {Object} options  Object literal with any of the Fx config options
10723     * @return {Roo.Element} The Element
10724     */
10725     shift : function(o){
10726         var el = this.getFxEl();
10727         o = o || {};
10728         el.queueFx(o, function(){
10729             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10730             if(w !== undefined){
10731                 a.width = {to: this.adjustWidth(w)};
10732             }
10733             if(h !== undefined){
10734                 a.height = {to: this.adjustHeight(h)};
10735             }
10736             if(x !== undefined || y !== undefined){
10737                 a.points = {to: [
10738                     x !== undefined ? x : this.getX(),
10739                     y !== undefined ? y : this.getY()
10740                 ]};
10741             }
10742             if(op !== undefined){
10743                 a.opacity = {to: op};
10744             }
10745             if(o.xy !== undefined){
10746                 a.points = {to: o.xy};
10747             }
10748             arguments.callee.anim = this.fxanim(a,
10749                 o, 'motion', .35, "easeOut", function(){
10750                 el.afterFx(o);
10751             });
10752         });
10753         return this;
10754     },
10755
10756         /**
10757          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10758          * ending point of the effect.
10759          * Usage:
10760          *<pre><code>
10761 // default: slide the element downward while fading out
10762 el.ghost();
10763
10764 // custom: slide the element out to the right with a 2-second duration
10765 el.ghost('r', { duration: 2 });
10766
10767 // common config options shown with default values
10768 el.ghost('b', {
10769     easing: 'easeOut',
10770     duration: .5
10771     remove: false,
10772     useDisplay: false
10773 });
10774 </code></pre>
10775          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10776          * @param {Object} options (optional) Object literal with any of the Fx config options
10777          * @return {Roo.Element} The Element
10778          */
10779     ghost : function(anchor, o){
10780         var el = this.getFxEl();
10781         o = o || {};
10782
10783         el.queueFx(o, function(){
10784             anchor = anchor || "b";
10785
10786             // restore values after effect
10787             var r = this.getFxRestore();
10788             var w = this.getWidth(),
10789                 h = this.getHeight();
10790
10791             var st = this.dom.style;
10792
10793             var after = function(){
10794                 if(o.useDisplay){
10795                     el.setDisplayed(false);
10796                 }else{
10797                     el.hide();
10798                 }
10799
10800                 el.clearOpacity();
10801                 el.setPositioning(r.pos);
10802                 st.width = r.width;
10803                 st.height = r.height;
10804
10805                 el.afterFx(o);
10806             };
10807
10808             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10809             switch(anchor.toLowerCase()){
10810                 case "t":
10811                     pt.by = [0, -h];
10812                 break;
10813                 case "l":
10814                     pt.by = [-w, 0];
10815                 break;
10816                 case "r":
10817                     pt.by = [w, 0];
10818                 break;
10819                 case "b":
10820                     pt.by = [0, h];
10821                 break;
10822                 case "tl":
10823                     pt.by = [-w, -h];
10824                 break;
10825                 case "bl":
10826                     pt.by = [-w, h];
10827                 break;
10828                 case "br":
10829                     pt.by = [w, h];
10830                 break;
10831                 case "tr":
10832                     pt.by = [w, -h];
10833                 break;
10834             }
10835
10836             arguments.callee.anim = this.fxanim(a,
10837                 o,
10838                 'motion',
10839                 .5,
10840                 "easeOut", after);
10841         });
10842         return this;
10843     },
10844
10845         /**
10846          * Ensures that all effects queued after syncFx is called on the element are
10847          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10848          * @return {Roo.Element} The Element
10849          */
10850     syncFx : function(){
10851         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10852             block : false,
10853             concurrent : true,
10854             stopFx : false
10855         });
10856         return this;
10857     },
10858
10859         /**
10860          * Ensures that all effects queued after sequenceFx is called on the element are
10861          * run in sequence.  This is the opposite of {@link #syncFx}.
10862          * @return {Roo.Element} The Element
10863          */
10864     sequenceFx : function(){
10865         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10866             block : false,
10867             concurrent : false,
10868             stopFx : false
10869         });
10870         return this;
10871     },
10872
10873         /* @private */
10874     nextFx : function(){
10875         var ef = this.fxQueue[0];
10876         if(ef){
10877             ef.call(this);
10878         }
10879     },
10880
10881         /**
10882          * Returns true if the element has any effects actively running or queued, else returns false.
10883          * @return {Boolean} True if element has active effects, else false
10884          */
10885     hasActiveFx : function(){
10886         return this.fxQueue && this.fxQueue[0];
10887     },
10888
10889         /**
10890          * Stops any running effects and clears the element's internal effects queue if it contains
10891          * any additional effects that haven't started yet.
10892          * @return {Roo.Element} The Element
10893          */
10894     stopFx : function(){
10895         if(this.hasActiveFx()){
10896             var cur = this.fxQueue[0];
10897             if(cur && cur.anim && cur.anim.isAnimated()){
10898                 this.fxQueue = [cur]; // clear out others
10899                 cur.anim.stop(true);
10900             }
10901         }
10902         return this;
10903     },
10904
10905         /* @private */
10906     beforeFx : function(o){
10907         if(this.hasActiveFx() && !o.concurrent){
10908            if(o.stopFx){
10909                this.stopFx();
10910                return true;
10911            }
10912            return false;
10913         }
10914         return true;
10915     },
10916
10917         /**
10918          * Returns true if the element is currently blocking so that no other effect can be queued
10919          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10920          * used to ensure that an effect initiated by a user action runs to completion prior to the
10921          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10922          * @return {Boolean} True if blocking, else false
10923          */
10924     hasFxBlock : function(){
10925         var q = this.fxQueue;
10926         return q && q[0] && q[0].block;
10927     },
10928
10929         /* @private */
10930     queueFx : function(o, fn){
10931         if(!this.fxQueue){
10932             this.fxQueue = [];
10933         }
10934         if(!this.hasFxBlock()){
10935             Roo.applyIf(o, this.fxDefaults);
10936             if(!o.concurrent){
10937                 var run = this.beforeFx(o);
10938                 fn.block = o.block;
10939                 this.fxQueue.push(fn);
10940                 if(run){
10941                     this.nextFx();
10942                 }
10943             }else{
10944                 fn.call(this);
10945             }
10946         }
10947         return this;
10948     },
10949
10950         /* @private */
10951     fxWrap : function(pos, o, vis){
10952         var wrap;
10953         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10954             var wrapXY;
10955             if(o.fixPosition){
10956                 wrapXY = this.getXY();
10957             }
10958             var div = document.createElement("div");
10959             div.style.visibility = vis;
10960             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10961             wrap.setPositioning(pos);
10962             if(wrap.getStyle("position") == "static"){
10963                 wrap.position("relative");
10964             }
10965             this.clearPositioning('auto');
10966             wrap.clip();
10967             wrap.dom.appendChild(this.dom);
10968             if(wrapXY){
10969                 wrap.setXY(wrapXY);
10970             }
10971         }
10972         return wrap;
10973     },
10974
10975         /* @private */
10976     fxUnwrap : function(wrap, pos, o){
10977         this.clearPositioning();
10978         this.setPositioning(pos);
10979         if(!o.wrap){
10980             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10981             wrap.remove();
10982         }
10983     },
10984
10985         /* @private */
10986     getFxRestore : function(){
10987         var st = this.dom.style;
10988         return {pos: this.getPositioning(), width: st.width, height : st.height};
10989     },
10990
10991         /* @private */
10992     afterFx : function(o){
10993         if(o.afterStyle){
10994             this.applyStyles(o.afterStyle);
10995         }
10996         if(o.afterCls){
10997             this.addClass(o.afterCls);
10998         }
10999         if(o.remove === true){
11000             this.remove();
11001         }
11002         Roo.callback(o.callback, o.scope, [this]);
11003         if(!o.concurrent){
11004             this.fxQueue.shift();
11005             this.nextFx();
11006         }
11007     },
11008
11009         /* @private */
11010     getFxEl : function(){ // support for composite element fx
11011         return Roo.get(this.dom);
11012     },
11013
11014         /* @private */
11015     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11016         animType = animType || 'run';
11017         opt = opt || {};
11018         var anim = Roo.lib.Anim[animType](
11019             this.dom, args,
11020             (opt.duration || defaultDur) || .35,
11021             (opt.easing || defaultEase) || 'easeOut',
11022             function(){
11023                 Roo.callback(cb, this);
11024             },
11025             this
11026         );
11027         opt.anim = anim;
11028         return anim;
11029     }
11030 };
11031
11032 // backwords compat
11033 Roo.Fx.resize = Roo.Fx.scale;
11034
11035 //When included, Roo.Fx is automatically applied to Element so that all basic
11036 //effects are available directly via the Element API
11037 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11038  * Based on:
11039  * Ext JS Library 1.1.1
11040  * Copyright(c) 2006-2007, Ext JS, LLC.
11041  *
11042  * Originally Released Under LGPL - original licence link has changed is not relivant.
11043  *
11044  * Fork - LGPL
11045  * <script type="text/javascript">
11046  */
11047
11048
11049 /**
11050  * @class Roo.CompositeElement
11051  * Standard composite class. Creates a Roo.Element for every element in the collection.
11052  * <br><br>
11053  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11054  * actions will be performed on all the elements in this collection.</b>
11055  * <br><br>
11056  * All methods return <i>this</i> and can be chained.
11057  <pre><code>
11058  var els = Roo.select("#some-el div.some-class", true);
11059  // or select directly from an existing element
11060  var el = Roo.get('some-el');
11061  el.select('div.some-class', true);
11062
11063  els.setWidth(100); // all elements become 100 width
11064  els.hide(true); // all elements fade out and hide
11065  // or
11066  els.setWidth(100).hide(true);
11067  </code></pre>
11068  */
11069 Roo.CompositeElement = function(els){
11070     this.elements = [];
11071     this.addElements(els);
11072 };
11073 Roo.CompositeElement.prototype = {
11074     isComposite: true,
11075     addElements : function(els){
11076         if(!els) {
11077             return this;
11078         }
11079         if(typeof els == "string"){
11080             els = Roo.Element.selectorFunction(els);
11081         }
11082         var yels = this.elements;
11083         var index = yels.length-1;
11084         for(var i = 0, len = els.length; i < len; i++) {
11085                 yels[++index] = Roo.get(els[i]);
11086         }
11087         return this;
11088     },
11089
11090     /**
11091     * Clears this composite and adds the elements returned by the passed selector.
11092     * @param {String/Array} els A string CSS selector, an array of elements or an element
11093     * @return {CompositeElement} this
11094     */
11095     fill : function(els){
11096         this.elements = [];
11097         this.add(els);
11098         return this;
11099     },
11100
11101     /**
11102     * Filters this composite to only elements that match the passed selector.
11103     * @param {String} selector A string CSS selector
11104     * @param {Boolean} inverse return inverse filter (not matches)
11105     * @return {CompositeElement} this
11106     */
11107     filter : function(selector, inverse){
11108         var els = [];
11109         inverse = inverse || false;
11110         this.each(function(el){
11111             var match = inverse ? !el.is(selector) : el.is(selector);
11112             if(match){
11113                 els[els.length] = el.dom;
11114             }
11115         });
11116         this.fill(els);
11117         return this;
11118     },
11119
11120     invoke : function(fn, args){
11121         var els = this.elements;
11122         for(var i = 0, len = els.length; i < len; i++) {
11123                 Roo.Element.prototype[fn].apply(els[i], args);
11124         }
11125         return this;
11126     },
11127     /**
11128     * Adds elements to this composite.
11129     * @param {String/Array} els A string CSS selector, an array of elements or an element
11130     * @return {CompositeElement} this
11131     */
11132     add : function(els){
11133         if(typeof els == "string"){
11134             this.addElements(Roo.Element.selectorFunction(els));
11135         }else if(els.length !== undefined){
11136             this.addElements(els);
11137         }else{
11138             this.addElements([els]);
11139         }
11140         return this;
11141     },
11142     /**
11143     * Calls the passed function passing (el, this, index) for each element in this composite.
11144     * @param {Function} fn The function to call
11145     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11146     * @return {CompositeElement} this
11147     */
11148     each : function(fn, scope){
11149         var els = this.elements;
11150         for(var i = 0, len = els.length; i < len; i++){
11151             if(fn.call(scope || els[i], els[i], this, i) === false) {
11152                 break;
11153             }
11154         }
11155         return this;
11156     },
11157
11158     /**
11159      * Returns the Element object at the specified index
11160      * @param {Number} index
11161      * @return {Roo.Element}
11162      */
11163     item : function(index){
11164         return this.elements[index] || null;
11165     },
11166
11167     /**
11168      * Returns the first Element
11169      * @return {Roo.Element}
11170      */
11171     first : function(){
11172         return this.item(0);
11173     },
11174
11175     /**
11176      * Returns the last Element
11177      * @return {Roo.Element}
11178      */
11179     last : function(){
11180         return this.item(this.elements.length-1);
11181     },
11182
11183     /**
11184      * Returns the number of elements in this composite
11185      * @return Number
11186      */
11187     getCount : function(){
11188         return this.elements.length;
11189     },
11190
11191     /**
11192      * Returns true if this composite contains the passed element
11193      * @return Boolean
11194      */
11195     contains : function(el){
11196         return this.indexOf(el) !== -1;
11197     },
11198
11199     /**
11200      * Returns true if this composite contains the passed element
11201      * @return Boolean
11202      */
11203     indexOf : function(el){
11204         return this.elements.indexOf(Roo.get(el));
11205     },
11206
11207
11208     /**
11209     * Removes the specified element(s).
11210     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11211     * or an array of any of those.
11212     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11213     * @return {CompositeElement} this
11214     */
11215     removeElement : function(el, removeDom){
11216         if(el instanceof Array){
11217             for(var i = 0, len = el.length; i < len; i++){
11218                 this.removeElement(el[i]);
11219             }
11220             return this;
11221         }
11222         var index = typeof el == 'number' ? el : this.indexOf(el);
11223         if(index !== -1){
11224             if(removeDom){
11225                 var d = this.elements[index];
11226                 if(d.dom){
11227                     d.remove();
11228                 }else{
11229                     d.parentNode.removeChild(d);
11230                 }
11231             }
11232             this.elements.splice(index, 1);
11233         }
11234         return this;
11235     },
11236
11237     /**
11238     * Replaces the specified element with the passed element.
11239     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11240     * to replace.
11241     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11242     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11243     * @return {CompositeElement} this
11244     */
11245     replaceElement : function(el, replacement, domReplace){
11246         var index = typeof el == 'number' ? el : this.indexOf(el);
11247         if(index !== -1){
11248             if(domReplace){
11249                 this.elements[index].replaceWith(replacement);
11250             }else{
11251                 this.elements.splice(index, 1, Roo.get(replacement))
11252             }
11253         }
11254         return this;
11255     },
11256
11257     /**
11258      * Removes all elements.
11259      */
11260     clear : function(){
11261         this.elements = [];
11262     }
11263 };
11264 (function(){
11265     Roo.CompositeElement.createCall = function(proto, fnName){
11266         if(!proto[fnName]){
11267             proto[fnName] = function(){
11268                 return this.invoke(fnName, arguments);
11269             };
11270         }
11271     };
11272     for(var fnName in Roo.Element.prototype){
11273         if(typeof Roo.Element.prototype[fnName] == "function"){
11274             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11275         }
11276     };
11277 })();
11278 /*
11279  * Based on:
11280  * Ext JS Library 1.1.1
11281  * Copyright(c) 2006-2007, Ext JS, LLC.
11282  *
11283  * Originally Released Under LGPL - original licence link has changed is not relivant.
11284  *
11285  * Fork - LGPL
11286  * <script type="text/javascript">
11287  */
11288
11289 /**
11290  * @class Roo.CompositeElementLite
11291  * @extends Roo.CompositeElement
11292  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11293  <pre><code>
11294  var els = Roo.select("#some-el div.some-class");
11295  // or select directly from an existing element
11296  var el = Roo.get('some-el');
11297  el.select('div.some-class');
11298
11299  els.setWidth(100); // all elements become 100 width
11300  els.hide(true); // all elements fade out and hide
11301  // or
11302  els.setWidth(100).hide(true);
11303  </code></pre><br><br>
11304  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11305  * actions will be performed on all the elements in this collection.</b>
11306  */
11307 Roo.CompositeElementLite = function(els){
11308     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11309     this.el = new Roo.Element.Flyweight();
11310 };
11311 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11312     addElements : function(els){
11313         if(els){
11314             if(els instanceof Array){
11315                 this.elements = this.elements.concat(els);
11316             }else{
11317                 var yels = this.elements;
11318                 var index = yels.length-1;
11319                 for(var i = 0, len = els.length; i < len; i++) {
11320                     yels[++index] = els[i];
11321                 }
11322             }
11323         }
11324         return this;
11325     },
11326     invoke : function(fn, args){
11327         var els = this.elements;
11328         var el = this.el;
11329         for(var i = 0, len = els.length; i < len; i++) {
11330             el.dom = els[i];
11331                 Roo.Element.prototype[fn].apply(el, args);
11332         }
11333         return this;
11334     },
11335     /**
11336      * Returns a flyweight Element of the dom element object at the specified index
11337      * @param {Number} index
11338      * @return {Roo.Element}
11339      */
11340     item : function(index){
11341         if(!this.elements[index]){
11342             return null;
11343         }
11344         this.el.dom = this.elements[index];
11345         return this.el;
11346     },
11347
11348     // fixes scope with flyweight
11349     addListener : function(eventName, handler, scope, opt){
11350         var els = this.elements;
11351         for(var i = 0, len = els.length; i < len; i++) {
11352             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11353         }
11354         return this;
11355     },
11356
11357     /**
11358     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11359     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11360     * a reference to the dom node, use el.dom.</b>
11361     * @param {Function} fn The function to call
11362     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11363     * @return {CompositeElement} this
11364     */
11365     each : function(fn, scope){
11366         var els = this.elements;
11367         var el = this.el;
11368         for(var i = 0, len = els.length; i < len; i++){
11369             el.dom = els[i];
11370                 if(fn.call(scope || el, el, this, i) === false){
11371                 break;
11372             }
11373         }
11374         return this;
11375     },
11376
11377     indexOf : function(el){
11378         return this.elements.indexOf(Roo.getDom(el));
11379     },
11380
11381     replaceElement : function(el, replacement, domReplace){
11382         var index = typeof el == 'number' ? el : this.indexOf(el);
11383         if(index !== -1){
11384             replacement = Roo.getDom(replacement);
11385             if(domReplace){
11386                 var d = this.elements[index];
11387                 d.parentNode.insertBefore(replacement, d);
11388                 d.parentNode.removeChild(d);
11389             }
11390             this.elements.splice(index, 1, replacement);
11391         }
11392         return this;
11393     }
11394 });
11395 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11396
11397 /*
11398  * Based on:
11399  * Ext JS Library 1.1.1
11400  * Copyright(c) 2006-2007, Ext JS, LLC.
11401  *
11402  * Originally Released Under LGPL - original licence link has changed is not relivant.
11403  *
11404  * Fork - LGPL
11405  * <script type="text/javascript">
11406  */
11407
11408  
11409
11410 /**
11411  * @class Roo.data.Connection
11412  * @extends Roo.util.Observable
11413  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11414  * either to a configured URL, or to a URL specified at request time.<br><br>
11415  * <p>
11416  * Requests made by this class are asynchronous, and will return immediately. No data from
11417  * the server will be available to the statement immediately following the {@link #request} call.
11418  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11419  * <p>
11420  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11421  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11422  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11423  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11424  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11425  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11426  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11427  * standard DOM methods.
11428  * @constructor
11429  * @param {Object} config a configuration object.
11430  */
11431 Roo.data.Connection = function(config){
11432     Roo.apply(this, config);
11433     this.addEvents({
11434         /**
11435          * @event beforerequest
11436          * Fires before a network request is made to retrieve a data object.
11437          * @param {Connection} conn This Connection object.
11438          * @param {Object} options The options config object passed to the {@link #request} method.
11439          */
11440         "beforerequest" : true,
11441         /**
11442          * @event requestcomplete
11443          * Fires if the request was successfully completed.
11444          * @param {Connection} conn This Connection object.
11445          * @param {Object} response The XHR object containing the response data.
11446          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11447          * @param {Object} options The options config object passed to the {@link #request} method.
11448          */
11449         "requestcomplete" : true,
11450         /**
11451          * @event requestexception
11452          * Fires if an error HTTP status was returned from the server.
11453          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11454          * @param {Connection} conn This Connection object.
11455          * @param {Object} response The XHR object containing the response data.
11456          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11457          * @param {Object} options The options config object passed to the {@link #request} method.
11458          */
11459         "requestexception" : true
11460     });
11461     Roo.data.Connection.superclass.constructor.call(this);
11462 };
11463
11464 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11465     /**
11466      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11467      */
11468     /**
11469      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11470      * extra parameters to each request made by this object. (defaults to undefined)
11471      */
11472     /**
11473      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11474      *  to each request made by this object. (defaults to undefined)
11475      */
11476     /**
11477      * @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)
11478      */
11479     /**
11480      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11481      */
11482     timeout : 30000,
11483     /**
11484      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11485      * @type Boolean
11486      */
11487     autoAbort:false,
11488
11489     /**
11490      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11491      * @type Boolean
11492      */
11493     disableCaching: true,
11494
11495     /**
11496      * Sends an HTTP request to a remote server.
11497      * @param {Object} options An object which may contain the following properties:<ul>
11498      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11499      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11500      * request, a url encoded string or a function to call to get either.</li>
11501      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11502      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11503      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11504      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11505      * <li>options {Object} The parameter to the request call.</li>
11506      * <li>success {Boolean} True if the request succeeded.</li>
11507      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11508      * </ul></li>
11509      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11510      * The callback is passed the following parameters:<ul>
11511      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11512      * <li>options {Object} The parameter to the request call.</li>
11513      * </ul></li>
11514      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11515      * The callback is passed the following parameters:<ul>
11516      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11517      * <li>options {Object} The parameter to the request call.</li>
11518      * </ul></li>
11519      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11520      * for the callback function. Defaults to the browser window.</li>
11521      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11522      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11523      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11524      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11525      * params for the post data. Any params will be appended to the URL.</li>
11526      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11527      * </ul>
11528      * @return {Number} transactionId
11529      */
11530     request : function(o){
11531         if(this.fireEvent("beforerequest", this, o) !== false){
11532             var p = o.params;
11533
11534             if(typeof p == "function"){
11535                 p = p.call(o.scope||window, o);
11536             }
11537             if(typeof p == "object"){
11538                 p = Roo.urlEncode(o.params);
11539             }
11540             if(this.extraParams){
11541                 var extras = Roo.urlEncode(this.extraParams);
11542                 p = p ? (p + '&' + extras) : extras;
11543             }
11544
11545             var url = o.url || this.url;
11546             if(typeof url == 'function'){
11547                 url = url.call(o.scope||window, o);
11548             }
11549
11550             if(o.form){
11551                 var form = Roo.getDom(o.form);
11552                 url = url || form.action;
11553
11554                 var enctype = form.getAttribute("enctype");
11555                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11556                     return this.doFormUpload(o, p, url);
11557                 }
11558                 var f = Roo.lib.Ajax.serializeForm(form);
11559                 p = p ? (p + '&' + f) : f;
11560             }
11561
11562             var hs = o.headers;
11563             if(this.defaultHeaders){
11564                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11565                 if(!o.headers){
11566                     o.headers = hs;
11567                 }
11568             }
11569
11570             var cb = {
11571                 success: this.handleResponse,
11572                 failure: this.handleFailure,
11573                 scope: this,
11574                 argument: {options: o},
11575                 timeout : o.timeout || this.timeout
11576             };
11577
11578             var method = o.method||this.method||(p ? "POST" : "GET");
11579
11580             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11581                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11582             }
11583
11584             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11585                 if(o.autoAbort){
11586                     this.abort();
11587                 }
11588             }else if(this.autoAbort !== false){
11589                 this.abort();
11590             }
11591
11592             if((method == 'GET' && p) || o.xmlData){
11593                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11594                 p = '';
11595             }
11596             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11597             return this.transId;
11598         }else{
11599             Roo.callback(o.callback, o.scope, [o, null, null]);
11600             return null;
11601         }
11602     },
11603
11604     /**
11605      * Determine whether this object has a request outstanding.
11606      * @param {Number} transactionId (Optional) defaults to the last transaction
11607      * @return {Boolean} True if there is an outstanding request.
11608      */
11609     isLoading : function(transId){
11610         if(transId){
11611             return Roo.lib.Ajax.isCallInProgress(transId);
11612         }else{
11613             return this.transId ? true : false;
11614         }
11615     },
11616
11617     /**
11618      * Aborts any outstanding request.
11619      * @param {Number} transactionId (Optional) defaults to the last transaction
11620      */
11621     abort : function(transId){
11622         if(transId || this.isLoading()){
11623             Roo.lib.Ajax.abort(transId || this.transId);
11624         }
11625     },
11626
11627     // private
11628     handleResponse : function(response){
11629         this.transId = false;
11630         var options = response.argument.options;
11631         response.argument = options ? options.argument : null;
11632         this.fireEvent("requestcomplete", this, response, options);
11633         Roo.callback(options.success, options.scope, [response, options]);
11634         Roo.callback(options.callback, options.scope, [options, true, response]);
11635     },
11636
11637     // private
11638     handleFailure : function(response, e){
11639         this.transId = false;
11640         var options = response.argument.options;
11641         response.argument = options ? options.argument : null;
11642         this.fireEvent("requestexception", this, response, options, e);
11643         Roo.callback(options.failure, options.scope, [response, options]);
11644         Roo.callback(options.callback, options.scope, [options, false, response]);
11645     },
11646
11647     // private
11648     doFormUpload : function(o, ps, url){
11649         var id = Roo.id();
11650         var frame = document.createElement('iframe');
11651         frame.id = id;
11652         frame.name = id;
11653         frame.className = 'x-hidden';
11654         if(Roo.isIE){
11655             frame.src = Roo.SSL_SECURE_URL;
11656         }
11657         document.body.appendChild(frame);
11658
11659         if(Roo.isIE){
11660            document.frames[id].name = id;
11661         }
11662
11663         var form = Roo.getDom(o.form);
11664         form.target = id;
11665         form.method = 'POST';
11666         form.enctype = form.encoding = 'multipart/form-data';
11667         if(url){
11668             form.action = url;
11669         }
11670
11671         var hiddens, hd;
11672         if(ps){ // add dynamic params
11673             hiddens = [];
11674             ps = Roo.urlDecode(ps, false);
11675             for(var k in ps){
11676                 if(ps.hasOwnProperty(k)){
11677                     hd = document.createElement('input');
11678                     hd.type = 'hidden';
11679                     hd.name = k;
11680                     hd.value = ps[k];
11681                     form.appendChild(hd);
11682                     hiddens.push(hd);
11683                 }
11684             }
11685         }
11686
11687         function cb(){
11688             var r = {  // bogus response object
11689                 responseText : '',
11690                 responseXML : null
11691             };
11692
11693             r.argument = o ? o.argument : null;
11694
11695             try { //
11696                 var doc;
11697                 if(Roo.isIE){
11698                     doc = frame.contentWindow.document;
11699                 }else {
11700                     doc = (frame.contentDocument || window.frames[id].document);
11701                 }
11702                 if(doc && doc.body){
11703                     r.responseText = doc.body.innerHTML;
11704                 }
11705                 if(doc && doc.XMLDocument){
11706                     r.responseXML = doc.XMLDocument;
11707                 }else {
11708                     r.responseXML = doc;
11709                 }
11710             }
11711             catch(e) {
11712                 // ignore
11713             }
11714
11715             Roo.EventManager.removeListener(frame, 'load', cb, this);
11716
11717             this.fireEvent("requestcomplete", this, r, o);
11718             Roo.callback(o.success, o.scope, [r, o]);
11719             Roo.callback(o.callback, o.scope, [o, true, r]);
11720
11721             setTimeout(function(){document.body.removeChild(frame);}, 100);
11722         }
11723
11724         Roo.EventManager.on(frame, 'load', cb, this);
11725         form.submit();
11726
11727         if(hiddens){ // remove dynamic params
11728             for(var i = 0, len = hiddens.length; i < len; i++){
11729                 form.removeChild(hiddens[i]);
11730             }
11731         }
11732     }
11733 });
11734 /*
11735  * Based on:
11736  * Ext JS Library 1.1.1
11737  * Copyright(c) 2006-2007, Ext JS, LLC.
11738  *
11739  * Originally Released Under LGPL - original licence link has changed is not relivant.
11740  *
11741  * Fork - LGPL
11742  * <script type="text/javascript">
11743  */
11744  
11745 /**
11746  * Global Ajax request class.
11747  * 
11748  * @class Roo.Ajax
11749  * @extends Roo.data.Connection
11750  * @static
11751  * 
11752  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11753  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11754  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11755  * @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)
11756  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11757  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11758  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11759  */
11760 Roo.Ajax = new Roo.data.Connection({
11761     // fix up the docs
11762     /**
11763      * @scope Roo.Ajax
11764      * @type {Boolear} 
11765      */
11766     autoAbort : false,
11767
11768     /**
11769      * Serialize the passed form into a url encoded string
11770      * @scope Roo.Ajax
11771      * @param {String/HTMLElement} form
11772      * @return {String}
11773      */
11774     serializeForm : function(form){
11775         return Roo.lib.Ajax.serializeForm(form);
11776     }
11777 });/*
11778  * Based on:
11779  * Ext JS Library 1.1.1
11780  * Copyright(c) 2006-2007, Ext JS, LLC.
11781  *
11782  * Originally Released Under LGPL - original licence link has changed is not relivant.
11783  *
11784  * Fork - LGPL
11785  * <script type="text/javascript">
11786  */
11787
11788  
11789 /**
11790  * @class Roo.UpdateManager
11791  * @extends Roo.util.Observable
11792  * Provides AJAX-style update for Element object.<br><br>
11793  * Usage:<br>
11794  * <pre><code>
11795  * // Get it from a Roo.Element object
11796  * var el = Roo.get("foo");
11797  * var mgr = el.getUpdateManager();
11798  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11799  * ...
11800  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11801  * <br>
11802  * // or directly (returns the same UpdateManager instance)
11803  * var mgr = new Roo.UpdateManager("myElementId");
11804  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11805  * mgr.on("update", myFcnNeedsToKnow);
11806  * <br>
11807    // short handed call directly from the element object
11808    Roo.get("foo").load({
11809         url: "bar.php",
11810         scripts:true,
11811         params: "for=bar",
11812         text: "Loading Foo..."
11813    });
11814  * </code></pre>
11815  * @constructor
11816  * Create new UpdateManager directly.
11817  * @param {String/HTMLElement/Roo.Element} el The element to update
11818  * @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).
11819  */
11820 Roo.UpdateManager = function(el, forceNew){
11821     el = Roo.get(el);
11822     if(!forceNew && el.updateManager){
11823         return el.updateManager;
11824     }
11825     /**
11826      * The Element object
11827      * @type Roo.Element
11828      */
11829     this.el = el;
11830     /**
11831      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11832      * @type String
11833      */
11834     this.defaultUrl = null;
11835
11836     this.addEvents({
11837         /**
11838          * @event beforeupdate
11839          * Fired before an update is made, return false from your handler and the update is cancelled.
11840          * @param {Roo.Element} el
11841          * @param {String/Object/Function} url
11842          * @param {String/Object} params
11843          */
11844         "beforeupdate": true,
11845         /**
11846          * @event update
11847          * Fired after successful update is made.
11848          * @param {Roo.Element} el
11849          * @param {Object} oResponseObject The response Object
11850          */
11851         "update": true,
11852         /**
11853          * @event failure
11854          * Fired on update failure.
11855          * @param {Roo.Element} el
11856          * @param {Object} oResponseObject The response Object
11857          */
11858         "failure": true
11859     });
11860     var d = Roo.UpdateManager.defaults;
11861     /**
11862      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11863      * @type String
11864      */
11865     this.sslBlankUrl = d.sslBlankUrl;
11866     /**
11867      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11868      * @type Boolean
11869      */
11870     this.disableCaching = d.disableCaching;
11871     /**
11872      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11873      * @type String
11874      */
11875     this.indicatorText = d.indicatorText;
11876     /**
11877      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11878      * @type String
11879      */
11880     this.showLoadIndicator = d.showLoadIndicator;
11881     /**
11882      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11883      * @type Number
11884      */
11885     this.timeout = d.timeout;
11886
11887     /**
11888      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11889      * @type Boolean
11890      */
11891     this.loadScripts = d.loadScripts;
11892
11893     /**
11894      * Transaction object of current executing transaction
11895      */
11896     this.transaction = null;
11897
11898     /**
11899      * @private
11900      */
11901     this.autoRefreshProcId = null;
11902     /**
11903      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11904      * @type Function
11905      */
11906     this.refreshDelegate = this.refresh.createDelegate(this);
11907     /**
11908      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11909      * @type Function
11910      */
11911     this.updateDelegate = this.update.createDelegate(this);
11912     /**
11913      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11914      * @type Function
11915      */
11916     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11917     /**
11918      * @private
11919      */
11920     this.successDelegate = this.processSuccess.createDelegate(this);
11921     /**
11922      * @private
11923      */
11924     this.failureDelegate = this.processFailure.createDelegate(this);
11925
11926     if(!this.renderer){
11927      /**
11928       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11929       */
11930     this.renderer = new Roo.UpdateManager.BasicRenderer();
11931     }
11932     
11933     Roo.UpdateManager.superclass.constructor.call(this);
11934 };
11935
11936 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11937     /**
11938      * Get the Element this UpdateManager is bound to
11939      * @return {Roo.Element} The element
11940      */
11941     getEl : function(){
11942         return this.el;
11943     },
11944     /**
11945      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11946      * @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:
11947 <pre><code>
11948 um.update({<br/>
11949     url: "your-url.php",<br/>
11950     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11951     callback: yourFunction,<br/>
11952     scope: yourObject, //(optional scope)  <br/>
11953     discardUrl: false, <br/>
11954     nocache: false,<br/>
11955     text: "Loading...",<br/>
11956     timeout: 30,<br/>
11957     scripts: false<br/>
11958 });
11959 </code></pre>
11960      * The only required property is url. The optional properties nocache, text and scripts
11961      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11962      * @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}
11963      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11964      * @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.
11965      */
11966     update : function(url, params, callback, discardUrl){
11967         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11968             var method = this.method,
11969                 cfg;
11970             if(typeof url == "object"){ // must be config object
11971                 cfg = url;
11972                 url = cfg.url;
11973                 params = params || cfg.params;
11974                 callback = callback || cfg.callback;
11975                 discardUrl = discardUrl || cfg.discardUrl;
11976                 if(callback && cfg.scope){
11977                     callback = callback.createDelegate(cfg.scope);
11978                 }
11979                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11980                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11981                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11982                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11983                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11984             }
11985             this.showLoading();
11986             if(!discardUrl){
11987                 this.defaultUrl = url;
11988             }
11989             if(typeof url == "function"){
11990                 url = url.call(this);
11991             }
11992
11993             method = method || (params ? "POST" : "GET");
11994             if(method == "GET"){
11995                 url = this.prepareUrl(url);
11996             }
11997
11998             var o = Roo.apply(cfg ||{}, {
11999                 url : url,
12000                 params: params,
12001                 success: this.successDelegate,
12002                 failure: this.failureDelegate,
12003                 callback: undefined,
12004                 timeout: (this.timeout*1000),
12005                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12006             });
12007             Roo.log("updated manager called with timeout of " + o.timeout);
12008             this.transaction = Roo.Ajax.request(o);
12009         }
12010     },
12011
12012     /**
12013      * 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.
12014      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12015      * @param {String/HTMLElement} form The form Id or form element
12016      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12017      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12018      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12019      */
12020     formUpdate : function(form, url, reset, callback){
12021         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12022             if(typeof url == "function"){
12023                 url = url.call(this);
12024             }
12025             form = Roo.getDom(form);
12026             this.transaction = Roo.Ajax.request({
12027                 form: form,
12028                 url:url,
12029                 success: this.successDelegate,
12030                 failure: this.failureDelegate,
12031                 timeout: (this.timeout*1000),
12032                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12033             });
12034             this.showLoading.defer(1, this);
12035         }
12036     },
12037
12038     /**
12039      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12040      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12041      */
12042     refresh : function(callback){
12043         if(this.defaultUrl == null){
12044             return;
12045         }
12046         this.update(this.defaultUrl, null, callback, true);
12047     },
12048
12049     /**
12050      * Set this element to auto refresh.
12051      * @param {Number} interval How often to update (in seconds).
12052      * @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)
12053      * @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}
12054      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12055      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12056      */
12057     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12058         if(refreshNow){
12059             this.update(url || this.defaultUrl, params, callback, true);
12060         }
12061         if(this.autoRefreshProcId){
12062             clearInterval(this.autoRefreshProcId);
12063         }
12064         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12065     },
12066
12067     /**
12068      * Stop auto refresh on this element.
12069      */
12070      stopAutoRefresh : function(){
12071         if(this.autoRefreshProcId){
12072             clearInterval(this.autoRefreshProcId);
12073             delete this.autoRefreshProcId;
12074         }
12075     },
12076
12077     isAutoRefreshing : function(){
12078        return this.autoRefreshProcId ? true : false;
12079     },
12080     /**
12081      * Called to update the element to "Loading" state. Override to perform custom action.
12082      */
12083     showLoading : function(){
12084         if(this.showLoadIndicator){
12085             this.el.update(this.indicatorText);
12086         }
12087     },
12088
12089     /**
12090      * Adds unique parameter to query string if disableCaching = true
12091      * @private
12092      */
12093     prepareUrl : function(url){
12094         if(this.disableCaching){
12095             var append = "_dc=" + (new Date().getTime());
12096             if(url.indexOf("?") !== -1){
12097                 url += "&" + append;
12098             }else{
12099                 url += "?" + append;
12100             }
12101         }
12102         return url;
12103     },
12104
12105     /**
12106      * @private
12107      */
12108     processSuccess : function(response){
12109         this.transaction = null;
12110         if(response.argument.form && response.argument.reset){
12111             try{ // put in try/catch since some older FF releases had problems with this
12112                 response.argument.form.reset();
12113             }catch(e){}
12114         }
12115         if(this.loadScripts){
12116             this.renderer.render(this.el, response, this,
12117                 this.updateComplete.createDelegate(this, [response]));
12118         }else{
12119             this.renderer.render(this.el, response, this);
12120             this.updateComplete(response);
12121         }
12122     },
12123
12124     updateComplete : function(response){
12125         this.fireEvent("update", this.el, response);
12126         if(typeof response.argument.callback == "function"){
12127             response.argument.callback(this.el, true, response);
12128         }
12129     },
12130
12131     /**
12132      * @private
12133      */
12134     processFailure : function(response){
12135         this.transaction = null;
12136         this.fireEvent("failure", this.el, response);
12137         if(typeof response.argument.callback == "function"){
12138             response.argument.callback(this.el, false, response);
12139         }
12140     },
12141
12142     /**
12143      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12144      * @param {Object} renderer The object implementing the render() method
12145      */
12146     setRenderer : function(renderer){
12147         this.renderer = renderer;
12148     },
12149
12150     getRenderer : function(){
12151        return this.renderer;
12152     },
12153
12154     /**
12155      * Set the defaultUrl used for updates
12156      * @param {String/Function} defaultUrl The url or a function to call to get the url
12157      */
12158     setDefaultUrl : function(defaultUrl){
12159         this.defaultUrl = defaultUrl;
12160     },
12161
12162     /**
12163      * Aborts the executing transaction
12164      */
12165     abort : function(){
12166         if(this.transaction){
12167             Roo.Ajax.abort(this.transaction);
12168         }
12169     },
12170
12171     /**
12172      * Returns true if an update is in progress
12173      * @return {Boolean}
12174      */
12175     isUpdating : function(){
12176         if(this.transaction){
12177             return Roo.Ajax.isLoading(this.transaction);
12178         }
12179         return false;
12180     }
12181 });
12182
12183 /**
12184  * @class Roo.UpdateManager.defaults
12185  * @static (not really - but it helps the doc tool)
12186  * The defaults collection enables customizing the default properties of UpdateManager
12187  */
12188    Roo.UpdateManager.defaults = {
12189        /**
12190          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12191          * @type Number
12192          */
12193          timeout : 30,
12194
12195          /**
12196          * True to process scripts by default (Defaults to false).
12197          * @type Boolean
12198          */
12199         loadScripts : false,
12200
12201         /**
12202         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12203         * @type String
12204         */
12205         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12206         /**
12207          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12208          * @type Boolean
12209          */
12210         disableCaching : false,
12211         /**
12212          * Whether to show indicatorText when loading (Defaults to true).
12213          * @type Boolean
12214          */
12215         showLoadIndicator : true,
12216         /**
12217          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12218          * @type String
12219          */
12220         indicatorText : '<div class="loading-indicator">Loading...</div>'
12221    };
12222
12223 /**
12224  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12225  *Usage:
12226  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12227  * @param {String/HTMLElement/Roo.Element} el The element to update
12228  * @param {String} url The url
12229  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12230  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12231  * @static
12232  * @deprecated
12233  * @member Roo.UpdateManager
12234  */
12235 Roo.UpdateManager.updateElement = function(el, url, params, options){
12236     var um = Roo.get(el, true).getUpdateManager();
12237     Roo.apply(um, options);
12238     um.update(url, params, options ? options.callback : null);
12239 };
12240 // alias for backwards compat
12241 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12242 /**
12243  * @class Roo.UpdateManager.BasicRenderer
12244  * Default Content renderer. Updates the elements innerHTML with the responseText.
12245  */
12246 Roo.UpdateManager.BasicRenderer = function(){};
12247
12248 Roo.UpdateManager.BasicRenderer.prototype = {
12249     /**
12250      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12251      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12252      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12253      * @param {Roo.Element} el The element being rendered
12254      * @param {Object} response The YUI Connect response object
12255      * @param {UpdateManager} updateManager The calling update manager
12256      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12257      */
12258      render : function(el, response, updateManager, callback){
12259         el.update(response.responseText, updateManager.loadScripts, callback);
12260     }
12261 };
12262 /*
12263  * Based on:
12264  * Roo JS
12265  * (c)) Alan Knowles
12266  * Licence : LGPL
12267  */
12268
12269
12270 /**
12271  * @class Roo.DomTemplate
12272  * @extends Roo.Template
12273  * An effort at a dom based template engine..
12274  *
12275  * Similar to XTemplate, except it uses dom parsing to create the template..
12276  *
12277  * Supported features:
12278  *
12279  *  Tags:
12280
12281 <pre><code>
12282       {a_variable} - output encoded.
12283       {a_variable.format:("Y-m-d")} - call a method on the variable
12284       {a_variable:raw} - unencoded output
12285       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12286       {a_variable:this.method_on_template(...)} - call a method on the template object.
12287  
12288 </code></pre>
12289  *  The tpl tag:
12290 <pre><code>
12291         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12292         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12293         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12294         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12295   
12296 </code></pre>
12297  *      
12298  */
12299 Roo.DomTemplate = function()
12300 {
12301      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12302      if (this.html) {
12303         this.compile();
12304      }
12305 };
12306
12307
12308 Roo.extend(Roo.DomTemplate, Roo.Template, {
12309     /**
12310      * id counter for sub templates.
12311      */
12312     id : 0,
12313     /**
12314      * flag to indicate if dom parser is inside a pre,
12315      * it will strip whitespace if not.
12316      */
12317     inPre : false,
12318     
12319     /**
12320      * The various sub templates
12321      */
12322     tpls : false,
12323     
12324     
12325     
12326     /**
12327      *
12328      * basic tag replacing syntax
12329      * WORD:WORD()
12330      *
12331      * // you can fake an object call by doing this
12332      *  x.t:(test,tesT) 
12333      * 
12334      */
12335     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12336     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12337     
12338     iterChild : function (node, method) {
12339         
12340         var oldPre = this.inPre;
12341         if (node.tagName == 'PRE') {
12342             this.inPre = true;
12343         }
12344         for( var i = 0; i < node.childNodes.length; i++) {
12345             method.call(this, node.childNodes[i]);
12346         }
12347         this.inPre = oldPre;
12348     },
12349     
12350     
12351     
12352     /**
12353      * compile the template
12354      *
12355      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12356      *
12357      */
12358     compile: function()
12359     {
12360         var s = this.html;
12361         
12362         // covert the html into DOM...
12363         var doc = false;
12364         var div =false;
12365         try {
12366             doc = document.implementation.createHTMLDocument("");
12367             doc.documentElement.innerHTML =   this.html  ;
12368             div = doc.documentElement;
12369         } catch (e) {
12370             // old IE... - nasty -- it causes all sorts of issues.. with
12371             // images getting pulled from server..
12372             div = document.createElement('div');
12373             div.innerHTML = this.html;
12374         }
12375         //doc.documentElement.innerHTML = htmlBody
12376          
12377         
12378         
12379         this.tpls = [];
12380         var _t = this;
12381         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12382         
12383         var tpls = this.tpls;
12384         
12385         // create a top level template from the snippet..
12386         
12387         //Roo.log(div.innerHTML);
12388         
12389         var tpl = {
12390             uid : 'master',
12391             id : this.id++,
12392             attr : false,
12393             value : false,
12394             body : div.innerHTML,
12395             
12396             forCall : false,
12397             execCall : false,
12398             dom : div,
12399             isTop : true
12400             
12401         };
12402         tpls.unshift(tpl);
12403         
12404         
12405         // compile them...
12406         this.tpls = [];
12407         Roo.each(tpls, function(tp){
12408             this.compileTpl(tp);
12409             this.tpls[tp.id] = tp;
12410         }, this);
12411         
12412         this.master = tpls[0];
12413         return this;
12414         
12415         
12416     },
12417     
12418     compileNode : function(node, istop) {
12419         // test for
12420         //Roo.log(node);
12421         
12422         
12423         // skip anything not a tag..
12424         if (node.nodeType != 1) {
12425             if (node.nodeType == 3 && !this.inPre) {
12426                 // reduce white space..
12427                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12428                 
12429             }
12430             return;
12431         }
12432         
12433         var tpl = {
12434             uid : false,
12435             id : false,
12436             attr : false,
12437             value : false,
12438             body : '',
12439             
12440             forCall : false,
12441             execCall : false,
12442             dom : false,
12443             isTop : istop
12444             
12445             
12446         };
12447         
12448         
12449         switch(true) {
12450             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12451             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12452             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12453             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12454             // no default..
12455         }
12456         
12457         
12458         if (!tpl.attr) {
12459             // just itterate children..
12460             this.iterChild(node,this.compileNode);
12461             return;
12462         }
12463         tpl.uid = this.id++;
12464         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12465         node.removeAttribute('roo-'+ tpl.attr);
12466         if (tpl.attr != 'name') {
12467             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12468             node.parentNode.replaceChild(placeholder,  node);
12469         } else {
12470             
12471             var placeholder =  document.createElement('span');
12472             placeholder.className = 'roo-tpl-' + tpl.value;
12473             node.parentNode.replaceChild(placeholder,  node);
12474         }
12475         
12476         // parent now sees '{domtplXXXX}
12477         this.iterChild(node,this.compileNode);
12478         
12479         // we should now have node body...
12480         var div = document.createElement('div');
12481         div.appendChild(node);
12482         tpl.dom = node;
12483         // this has the unfortunate side effect of converting tagged attributes
12484         // eg. href="{...}" into %7C...%7D
12485         // this has been fixed by searching for those combo's although it's a bit hacky..
12486         
12487         
12488         tpl.body = div.innerHTML;
12489         
12490         
12491          
12492         tpl.id = tpl.uid;
12493         switch(tpl.attr) {
12494             case 'for' :
12495                 switch (tpl.value) {
12496                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12497                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12498                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12499                 }
12500                 break;
12501             
12502             case 'exec':
12503                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12504                 break;
12505             
12506             case 'if':     
12507                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12508                 break;
12509             
12510             case 'name':
12511                 tpl.id  = tpl.value; // replace non characters???
12512                 break;
12513             
12514         }
12515         
12516         
12517         this.tpls.push(tpl);
12518         
12519         
12520         
12521     },
12522     
12523     
12524     
12525     
12526     /**
12527      * Compile a segment of the template into a 'sub-template'
12528      *
12529      * 
12530      * 
12531      *
12532      */
12533     compileTpl : function(tpl)
12534     {
12535         var fm = Roo.util.Format;
12536         var useF = this.disableFormats !== true;
12537         
12538         var sep = Roo.isGecko ? "+\n" : ",\n";
12539         
12540         var undef = function(str) {
12541             Roo.debug && Roo.log("Property not found :"  + str);
12542             return '';
12543         };
12544           
12545         //Roo.log(tpl.body);
12546         
12547         
12548         
12549         var fn = function(m, lbrace, name, format, args)
12550         {
12551             //Roo.log("ARGS");
12552             //Roo.log(arguments);
12553             args = args ? args.replace(/\\'/g,"'") : args;
12554             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12555             if (typeof(format) == 'undefined') {
12556                 format =  'htmlEncode'; 
12557             }
12558             if (format == 'raw' ) {
12559                 format = false;
12560             }
12561             
12562             if(name.substr(0, 6) == 'domtpl'){
12563                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12564             }
12565             
12566             // build an array of options to determine if value is undefined..
12567             
12568             // basically get 'xxxx.yyyy' then do
12569             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12570             //    (function () { Roo.log("Property not found"); return ''; })() :
12571             //    ......
12572             
12573             var udef_ar = [];
12574             var lookfor = '';
12575             Roo.each(name.split('.'), function(st) {
12576                 lookfor += (lookfor.length ? '.': '') + st;
12577                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12578             });
12579             
12580             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12581             
12582             
12583             if(format && useF){
12584                 
12585                 args = args ? ',' + args : "";
12586                  
12587                 if(format.substr(0, 5) != "this."){
12588                     format = "fm." + format + '(';
12589                 }else{
12590                     format = 'this.call("'+ format.substr(5) + '", ';
12591                     args = ", values";
12592                 }
12593                 
12594                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12595             }
12596              
12597             if (args && args.length) {
12598                 // called with xxyx.yuu:(test,test)
12599                 // change to ()
12600                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12601             }
12602             // raw.. - :raw modifier..
12603             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12604             
12605         };
12606         var body;
12607         // branched to use + in gecko and [].join() in others
12608         if(Roo.isGecko){
12609             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12610                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12611                     "';};};";
12612         }else{
12613             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12614             body.push(tpl.body.replace(/(\r\n|\n)/g,
12615                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12616             body.push("'].join('');};};");
12617             body = body.join('');
12618         }
12619         
12620         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12621        
12622         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12623         eval(body);
12624         
12625         return this;
12626     },
12627      
12628     /**
12629      * same as applyTemplate, except it's done to one of the subTemplates
12630      * when using named templates, you can do:
12631      *
12632      * var str = pl.applySubTemplate('your-name', values);
12633      *
12634      * 
12635      * @param {Number} id of the template
12636      * @param {Object} values to apply to template
12637      * @param {Object} parent (normaly the instance of this object)
12638      */
12639     applySubTemplate : function(id, values, parent)
12640     {
12641         
12642         
12643         var t = this.tpls[id];
12644         
12645         
12646         try { 
12647             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12648                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12649                 return '';
12650             }
12651         } catch(e) {
12652             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12653             Roo.log(values);
12654           
12655             return '';
12656         }
12657         try { 
12658             
12659             if(t.execCall && t.execCall.call(this, values, parent)){
12660                 return '';
12661             }
12662         } catch(e) {
12663             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12664             Roo.log(values);
12665             return '';
12666         }
12667         
12668         try {
12669             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12670             parent = t.target ? values : parent;
12671             if(t.forCall && vs instanceof Array){
12672                 var buf = [];
12673                 for(var i = 0, len = vs.length; i < len; i++){
12674                     try {
12675                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12676                     } catch (e) {
12677                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12678                         Roo.log(e.body);
12679                         //Roo.log(t.compiled);
12680                         Roo.log(vs[i]);
12681                     }   
12682                 }
12683                 return buf.join('');
12684             }
12685         } catch (e) {
12686             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12687             Roo.log(values);
12688             return '';
12689         }
12690         try {
12691             return t.compiled.call(this, vs, parent);
12692         } catch (e) {
12693             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12694             Roo.log(e.body);
12695             //Roo.log(t.compiled);
12696             Roo.log(values);
12697             return '';
12698         }
12699     },
12700
12701    
12702
12703     applyTemplate : function(values){
12704         return this.master.compiled.call(this, values, {});
12705         //var s = this.subs;
12706     },
12707
12708     apply : function(){
12709         return this.applyTemplate.apply(this, arguments);
12710     }
12711
12712  });
12713
12714 Roo.DomTemplate.from = function(el){
12715     el = Roo.getDom(el);
12716     return new Roo.Domtemplate(el.value || el.innerHTML);
12717 };/*
12718  * Based on:
12719  * Ext JS Library 1.1.1
12720  * Copyright(c) 2006-2007, Ext JS, LLC.
12721  *
12722  * Originally Released Under LGPL - original licence link has changed is not relivant.
12723  *
12724  * Fork - LGPL
12725  * <script type="text/javascript">
12726  */
12727
12728 /**
12729  * @class Roo.util.DelayedTask
12730  * Provides a convenient method of performing setTimeout where a new
12731  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12732  * You can use this class to buffer
12733  * the keypress events for a certain number of milliseconds, and perform only if they stop
12734  * for that amount of time.
12735  * @constructor The parameters to this constructor serve as defaults and are not required.
12736  * @param {Function} fn (optional) The default function to timeout
12737  * @param {Object} scope (optional) The default scope of that timeout
12738  * @param {Array} args (optional) The default Array of arguments
12739  */
12740 Roo.util.DelayedTask = function(fn, scope, args){
12741     var id = null, d, t;
12742
12743     var call = function(){
12744         var now = new Date().getTime();
12745         if(now - t >= d){
12746             clearInterval(id);
12747             id = null;
12748             fn.apply(scope, args || []);
12749         }
12750     };
12751     /**
12752      * Cancels any pending timeout and queues a new one
12753      * @param {Number} delay The milliseconds to delay
12754      * @param {Function} newFn (optional) Overrides function passed to constructor
12755      * @param {Object} newScope (optional) Overrides scope passed to constructor
12756      * @param {Array} newArgs (optional) Overrides args passed to constructor
12757      */
12758     this.delay = function(delay, newFn, newScope, newArgs){
12759         if(id && delay != d){
12760             this.cancel();
12761         }
12762         d = delay;
12763         t = new Date().getTime();
12764         fn = newFn || fn;
12765         scope = newScope || scope;
12766         args = newArgs || args;
12767         if(!id){
12768             id = setInterval(call, d);
12769         }
12770     };
12771
12772     /**
12773      * Cancel the last queued timeout
12774      */
12775     this.cancel = function(){
12776         if(id){
12777             clearInterval(id);
12778             id = null;
12779         }
12780     };
12781 };/*
12782  * Based on:
12783  * Ext JS Library 1.1.1
12784  * Copyright(c) 2006-2007, Ext JS, LLC.
12785  *
12786  * Originally Released Under LGPL - original licence link has changed is not relivant.
12787  *
12788  * Fork - LGPL
12789  * <script type="text/javascript">
12790  */
12791  
12792  
12793 Roo.util.TaskRunner = function(interval){
12794     interval = interval || 10;
12795     var tasks = [], removeQueue = [];
12796     var id = 0;
12797     var running = false;
12798
12799     var stopThread = function(){
12800         running = false;
12801         clearInterval(id);
12802         id = 0;
12803     };
12804
12805     var startThread = function(){
12806         if(!running){
12807             running = true;
12808             id = setInterval(runTasks, interval);
12809         }
12810     };
12811
12812     var removeTask = function(task){
12813         removeQueue.push(task);
12814         if(task.onStop){
12815             task.onStop();
12816         }
12817     };
12818
12819     var runTasks = function(){
12820         if(removeQueue.length > 0){
12821             for(var i = 0, len = removeQueue.length; i < len; i++){
12822                 tasks.remove(removeQueue[i]);
12823             }
12824             removeQueue = [];
12825             if(tasks.length < 1){
12826                 stopThread();
12827                 return;
12828             }
12829         }
12830         var now = new Date().getTime();
12831         for(var i = 0, len = tasks.length; i < len; ++i){
12832             var t = tasks[i];
12833             var itime = now - t.taskRunTime;
12834             if(t.interval <= itime){
12835                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12836                 t.taskRunTime = now;
12837                 if(rt === false || t.taskRunCount === t.repeat){
12838                     removeTask(t);
12839                     return;
12840                 }
12841             }
12842             if(t.duration && t.duration <= (now - t.taskStartTime)){
12843                 removeTask(t);
12844             }
12845         }
12846     };
12847
12848     /**
12849      * Queues a new task.
12850      * @param {Object} task
12851      */
12852     this.start = function(task){
12853         tasks.push(task);
12854         task.taskStartTime = new Date().getTime();
12855         task.taskRunTime = 0;
12856         task.taskRunCount = 0;
12857         startThread();
12858         return task;
12859     };
12860
12861     this.stop = function(task){
12862         removeTask(task);
12863         return task;
12864     };
12865
12866     this.stopAll = function(){
12867         stopThread();
12868         for(var i = 0, len = tasks.length; i < len; i++){
12869             if(tasks[i].onStop){
12870                 tasks[i].onStop();
12871             }
12872         }
12873         tasks = [];
12874         removeQueue = [];
12875     };
12876 };
12877
12878 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12879  * Based on:
12880  * Ext JS Library 1.1.1
12881  * Copyright(c) 2006-2007, Ext JS, LLC.
12882  *
12883  * Originally Released Under LGPL - original licence link has changed is not relivant.
12884  *
12885  * Fork - LGPL
12886  * <script type="text/javascript">
12887  */
12888
12889  
12890 /**
12891  * @class Roo.util.MixedCollection
12892  * @extends Roo.util.Observable
12893  * A Collection class that maintains both numeric indexes and keys and exposes events.
12894  * @constructor
12895  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12896  * collection (defaults to false)
12897  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12898  * and return the key value for that item.  This is used when available to look up the key on items that
12899  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12900  * equivalent to providing an implementation for the {@link #getKey} method.
12901  */
12902 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12903     this.items = [];
12904     this.map = {};
12905     this.keys = [];
12906     this.length = 0;
12907     this.addEvents({
12908         /**
12909          * @event clear
12910          * Fires when the collection is cleared.
12911          */
12912         "clear" : true,
12913         /**
12914          * @event add
12915          * Fires when an item is added to the collection.
12916          * @param {Number} index The index at which the item was added.
12917          * @param {Object} o The item added.
12918          * @param {String} key The key associated with the added item.
12919          */
12920         "add" : true,
12921         /**
12922          * @event replace
12923          * Fires when an item is replaced in the collection.
12924          * @param {String} key he key associated with the new added.
12925          * @param {Object} old The item being replaced.
12926          * @param {Object} new The new item.
12927          */
12928         "replace" : true,
12929         /**
12930          * @event remove
12931          * Fires when an item is removed from the collection.
12932          * @param {Object} o The item being removed.
12933          * @param {String} key (optional) The key associated with the removed item.
12934          */
12935         "remove" : true,
12936         "sort" : true
12937     });
12938     this.allowFunctions = allowFunctions === true;
12939     if(keyFn){
12940         this.getKey = keyFn;
12941     }
12942     Roo.util.MixedCollection.superclass.constructor.call(this);
12943 };
12944
12945 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12946     allowFunctions : false,
12947     
12948 /**
12949  * Adds an item to the collection.
12950  * @param {String} key The key to associate with the item
12951  * @param {Object} o The item to add.
12952  * @return {Object} The item added.
12953  */
12954     add : function(key, o){
12955         if(arguments.length == 1){
12956             o = arguments[0];
12957             key = this.getKey(o);
12958         }
12959         if(typeof key == "undefined" || key === null){
12960             this.length++;
12961             this.items.push(o);
12962             this.keys.push(null);
12963         }else{
12964             var old = this.map[key];
12965             if(old){
12966                 return this.replace(key, o);
12967             }
12968             this.length++;
12969             this.items.push(o);
12970             this.map[key] = o;
12971             this.keys.push(key);
12972         }
12973         this.fireEvent("add", this.length-1, o, key);
12974         return o;
12975     },
12976        
12977 /**
12978   * MixedCollection has a generic way to fetch keys if you implement getKey.
12979 <pre><code>
12980 // normal way
12981 var mc = new Roo.util.MixedCollection();
12982 mc.add(someEl.dom.id, someEl);
12983 mc.add(otherEl.dom.id, otherEl);
12984 //and so on
12985
12986 // using getKey
12987 var mc = new Roo.util.MixedCollection();
12988 mc.getKey = function(el){
12989    return el.dom.id;
12990 };
12991 mc.add(someEl);
12992 mc.add(otherEl);
12993
12994 // or via the constructor
12995 var mc = new Roo.util.MixedCollection(false, function(el){
12996    return el.dom.id;
12997 });
12998 mc.add(someEl);
12999 mc.add(otherEl);
13000 </code></pre>
13001  * @param o {Object} The item for which to find the key.
13002  * @return {Object} The key for the passed item.
13003  */
13004     getKey : function(o){
13005          return o.id; 
13006     },
13007    
13008 /**
13009  * Replaces an item in the collection.
13010  * @param {String} key The key associated with the item to replace, or the item to replace.
13011  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13012  * @return {Object}  The new item.
13013  */
13014     replace : function(key, o){
13015         if(arguments.length == 1){
13016             o = arguments[0];
13017             key = this.getKey(o);
13018         }
13019         var old = this.item(key);
13020         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13021              return this.add(key, o);
13022         }
13023         var index = this.indexOfKey(key);
13024         this.items[index] = o;
13025         this.map[key] = o;
13026         this.fireEvent("replace", key, old, o);
13027         return o;
13028     },
13029    
13030 /**
13031  * Adds all elements of an Array or an Object to the collection.
13032  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13033  * an Array of values, each of which are added to the collection.
13034  */
13035     addAll : function(objs){
13036         if(arguments.length > 1 || objs instanceof Array){
13037             var args = arguments.length > 1 ? arguments : objs;
13038             for(var i = 0, len = args.length; i < len; i++){
13039                 this.add(args[i]);
13040             }
13041         }else{
13042             for(var key in objs){
13043                 if(this.allowFunctions || typeof objs[key] != "function"){
13044                     this.add(key, objs[key]);
13045                 }
13046             }
13047         }
13048     },
13049    
13050 /**
13051  * Executes the specified function once for every item in the collection, passing each
13052  * item as the first and only parameter. returning false from the function will stop the iteration.
13053  * @param {Function} fn The function to execute for each item.
13054  * @param {Object} scope (optional) The scope in which to execute the function.
13055  */
13056     each : function(fn, scope){
13057         var items = [].concat(this.items); // each safe for removal
13058         for(var i = 0, len = items.length; i < len; i++){
13059             if(fn.call(scope || items[i], items[i], i, len) === false){
13060                 break;
13061             }
13062         }
13063     },
13064    
13065 /**
13066  * Executes the specified function once for every key in the collection, passing each
13067  * key, and its associated item as the first two parameters.
13068  * @param {Function} fn The function to execute for each item.
13069  * @param {Object} scope (optional) The scope in which to execute the function.
13070  */
13071     eachKey : function(fn, scope){
13072         for(var i = 0, len = this.keys.length; i < len; i++){
13073             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13074         }
13075     },
13076    
13077 /**
13078  * Returns the first item in the collection which elicits a true return value from the
13079  * passed selection function.
13080  * @param {Function} fn The selection function to execute for each item.
13081  * @param {Object} scope (optional) The scope in which to execute the function.
13082  * @return {Object} The first item in the collection which returned true from the selection function.
13083  */
13084     find : function(fn, scope){
13085         for(var i = 0, len = this.items.length; i < len; i++){
13086             if(fn.call(scope || window, this.items[i], this.keys[i])){
13087                 return this.items[i];
13088             }
13089         }
13090         return null;
13091     },
13092    
13093 /**
13094  * Inserts an item at the specified index in the collection.
13095  * @param {Number} index The index to insert the item at.
13096  * @param {String} key The key to associate with the new item, or the item itself.
13097  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13098  * @return {Object} The item inserted.
13099  */
13100     insert : function(index, key, o){
13101         if(arguments.length == 2){
13102             o = arguments[1];
13103             key = this.getKey(o);
13104         }
13105         if(index >= this.length){
13106             return this.add(key, o);
13107         }
13108         this.length++;
13109         this.items.splice(index, 0, o);
13110         if(typeof key != "undefined" && key != null){
13111             this.map[key] = o;
13112         }
13113         this.keys.splice(index, 0, key);
13114         this.fireEvent("add", index, o, key);
13115         return o;
13116     },
13117    
13118 /**
13119  * Removed an item from the collection.
13120  * @param {Object} o The item to remove.
13121  * @return {Object} The item removed.
13122  */
13123     remove : function(o){
13124         return this.removeAt(this.indexOf(o));
13125     },
13126    
13127 /**
13128  * Remove an item from a specified index in the collection.
13129  * @param {Number} index The index within the collection of the item to remove.
13130  */
13131     removeAt : function(index){
13132         if(index < this.length && index >= 0){
13133             this.length--;
13134             var o = this.items[index];
13135             this.items.splice(index, 1);
13136             var key = this.keys[index];
13137             if(typeof key != "undefined"){
13138                 delete this.map[key];
13139             }
13140             this.keys.splice(index, 1);
13141             this.fireEvent("remove", o, key);
13142         }
13143     },
13144    
13145 /**
13146  * Removed an item associated with the passed key fom the collection.
13147  * @param {String} key The key of the item to remove.
13148  */
13149     removeKey : function(key){
13150         return this.removeAt(this.indexOfKey(key));
13151     },
13152    
13153 /**
13154  * Returns the number of items in the collection.
13155  * @return {Number} the number of items in the collection.
13156  */
13157     getCount : function(){
13158         return this.length; 
13159     },
13160    
13161 /**
13162  * Returns index within the collection of the passed Object.
13163  * @param {Object} o The item to find the index of.
13164  * @return {Number} index of the item.
13165  */
13166     indexOf : function(o){
13167         if(!this.items.indexOf){
13168             for(var i = 0, len = this.items.length; i < len; i++){
13169                 if(this.items[i] == o) {
13170                     return i;
13171                 }
13172             }
13173             return -1;
13174         }else{
13175             return this.items.indexOf(o);
13176         }
13177     },
13178    
13179 /**
13180  * Returns index within the collection of the passed key.
13181  * @param {String} key The key to find the index of.
13182  * @return {Number} index of the key.
13183  */
13184     indexOfKey : function(key){
13185         if(!this.keys.indexOf){
13186             for(var i = 0, len = this.keys.length; i < len; i++){
13187                 if(this.keys[i] == key) {
13188                     return i;
13189                 }
13190             }
13191             return -1;
13192         }else{
13193             return this.keys.indexOf(key);
13194         }
13195     },
13196    
13197 /**
13198  * Returns the item associated with the passed key OR index. Key has priority over index.
13199  * @param {String/Number} key The key or index of the item.
13200  * @return {Object} The item associated with the passed key.
13201  */
13202     item : function(key){
13203         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13204         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13205     },
13206     
13207 /**
13208  * Returns the item at the specified index.
13209  * @param {Number} index The index of the item.
13210  * @return {Object}
13211  */
13212     itemAt : function(index){
13213         return this.items[index];
13214     },
13215     
13216 /**
13217  * Returns the item associated with the passed key.
13218  * @param {String/Number} key The key of the item.
13219  * @return {Object} The item associated with the passed key.
13220  */
13221     key : function(key){
13222         return this.map[key];
13223     },
13224    
13225 /**
13226  * Returns true if the collection contains the passed Object as an item.
13227  * @param {Object} o  The Object to look for in the collection.
13228  * @return {Boolean} True if the collection contains the Object as an item.
13229  */
13230     contains : function(o){
13231         return this.indexOf(o) != -1;
13232     },
13233    
13234 /**
13235  * Returns true if the collection contains the passed Object as a key.
13236  * @param {String} key The key to look for in the collection.
13237  * @return {Boolean} True if the collection contains the Object as a key.
13238  */
13239     containsKey : function(key){
13240         return typeof this.map[key] != "undefined";
13241     },
13242    
13243 /**
13244  * Removes all items from the collection.
13245  */
13246     clear : function(){
13247         this.length = 0;
13248         this.items = [];
13249         this.keys = [];
13250         this.map = {};
13251         this.fireEvent("clear");
13252     },
13253    
13254 /**
13255  * Returns the first item in the collection.
13256  * @return {Object} the first item in the collection..
13257  */
13258     first : function(){
13259         return this.items[0]; 
13260     },
13261    
13262 /**
13263  * Returns the last item in the collection.
13264  * @return {Object} the last item in the collection..
13265  */
13266     last : function(){
13267         return this.items[this.length-1];   
13268     },
13269     
13270     _sort : function(property, dir, fn){
13271         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13272         fn = fn || function(a, b){
13273             return a-b;
13274         };
13275         var c = [], k = this.keys, items = this.items;
13276         for(var i = 0, len = items.length; i < len; i++){
13277             c[c.length] = {key: k[i], value: items[i], index: i};
13278         }
13279         c.sort(function(a, b){
13280             var v = fn(a[property], b[property]) * dsc;
13281             if(v == 0){
13282                 v = (a.index < b.index ? -1 : 1);
13283             }
13284             return v;
13285         });
13286         for(var i = 0, len = c.length; i < len; i++){
13287             items[i] = c[i].value;
13288             k[i] = c[i].key;
13289         }
13290         this.fireEvent("sort", this);
13291     },
13292     
13293     /**
13294      * Sorts this collection with the passed comparison function
13295      * @param {String} direction (optional) "ASC" or "DESC"
13296      * @param {Function} fn (optional) comparison function
13297      */
13298     sort : function(dir, fn){
13299         this._sort("value", dir, fn);
13300     },
13301     
13302     /**
13303      * Sorts this collection by keys
13304      * @param {String} direction (optional) "ASC" or "DESC"
13305      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13306      */
13307     keySort : function(dir, fn){
13308         this._sort("key", dir, fn || function(a, b){
13309             return String(a).toUpperCase()-String(b).toUpperCase();
13310         });
13311     },
13312     
13313     /**
13314      * Returns a range of items in this collection
13315      * @param {Number} startIndex (optional) defaults to 0
13316      * @param {Number} endIndex (optional) default to the last item
13317      * @return {Array} An array of items
13318      */
13319     getRange : function(start, end){
13320         var items = this.items;
13321         if(items.length < 1){
13322             return [];
13323         }
13324         start = start || 0;
13325         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13326         var r = [];
13327         if(start <= end){
13328             for(var i = start; i <= end; i++) {
13329                     r[r.length] = items[i];
13330             }
13331         }else{
13332             for(var i = start; i >= end; i--) {
13333                     r[r.length] = items[i];
13334             }
13335         }
13336         return r;
13337     },
13338         
13339     /**
13340      * Filter the <i>objects</i> in this collection by a specific property. 
13341      * Returns a new collection that has been filtered.
13342      * @param {String} property A property on your objects
13343      * @param {String/RegExp} value Either string that the property values 
13344      * should start with or a RegExp to test against the property
13345      * @return {MixedCollection} The new filtered collection
13346      */
13347     filter : function(property, value){
13348         if(!value.exec){ // not a regex
13349             value = String(value);
13350             if(value.length == 0){
13351                 return this.clone();
13352             }
13353             value = new RegExp("^" + Roo.escapeRe(value), "i");
13354         }
13355         return this.filterBy(function(o){
13356             return o && value.test(o[property]);
13357         });
13358         },
13359     
13360     /**
13361      * Filter by a function. * Returns a new collection that has been filtered.
13362      * The passed function will be called with each 
13363      * object in the collection. If the function returns true, the value is included 
13364      * otherwise it is filtered.
13365      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13366      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13367      * @return {MixedCollection} The new filtered collection
13368      */
13369     filterBy : function(fn, scope){
13370         var r = new Roo.util.MixedCollection();
13371         r.getKey = this.getKey;
13372         var k = this.keys, it = this.items;
13373         for(var i = 0, len = it.length; i < len; i++){
13374             if(fn.call(scope||this, it[i], k[i])){
13375                                 r.add(k[i], it[i]);
13376                         }
13377         }
13378         return r;
13379     },
13380     
13381     /**
13382      * Creates a duplicate of this collection
13383      * @return {MixedCollection}
13384      */
13385     clone : function(){
13386         var r = new Roo.util.MixedCollection();
13387         var k = this.keys, it = this.items;
13388         for(var i = 0, len = it.length; i < len; i++){
13389             r.add(k[i], it[i]);
13390         }
13391         r.getKey = this.getKey;
13392         return r;
13393     }
13394 });
13395 /**
13396  * Returns the item associated with the passed key or index.
13397  * @method
13398  * @param {String/Number} key The key or index of the item.
13399  * @return {Object} The item associated with the passed key.
13400  */
13401 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13402  * Based on:
13403  * Ext JS Library 1.1.1
13404  * Copyright(c) 2006-2007, Ext JS, LLC.
13405  *
13406  * Originally Released Under LGPL - original licence link has changed is not relivant.
13407  *
13408  * Fork - LGPL
13409  * <script type="text/javascript">
13410  */
13411 /**
13412  * @class Roo.util.JSON
13413  * Modified version of Douglas Crockford"s json.js that doesn"t
13414  * mess with the Object prototype 
13415  * http://www.json.org/js.html
13416  * @singleton
13417  */
13418 Roo.util.JSON = new (function(){
13419     var useHasOwn = {}.hasOwnProperty ? true : false;
13420     
13421     // crashes Safari in some instances
13422     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13423     
13424     var pad = function(n) {
13425         return n < 10 ? "0" + n : n;
13426     };
13427     
13428     var m = {
13429         "\b": '\\b',
13430         "\t": '\\t',
13431         "\n": '\\n',
13432         "\f": '\\f',
13433         "\r": '\\r',
13434         '"' : '\\"',
13435         "\\": '\\\\'
13436     };
13437
13438     var encodeString = function(s){
13439         if (/["\\\x00-\x1f]/.test(s)) {
13440             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13441                 var c = m[b];
13442                 if(c){
13443                     return c;
13444                 }
13445                 c = b.charCodeAt();
13446                 return "\\u00" +
13447                     Math.floor(c / 16).toString(16) +
13448                     (c % 16).toString(16);
13449             }) + '"';
13450         }
13451         return '"' + s + '"';
13452     };
13453     
13454     var encodeArray = function(o){
13455         var a = ["["], b, i, l = o.length, v;
13456             for (i = 0; i < l; i += 1) {
13457                 v = o[i];
13458                 switch (typeof v) {
13459                     case "undefined":
13460                     case "function":
13461                     case "unknown":
13462                         break;
13463                     default:
13464                         if (b) {
13465                             a.push(',');
13466                         }
13467                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13468                         b = true;
13469                 }
13470             }
13471             a.push("]");
13472             return a.join("");
13473     };
13474     
13475     var encodeDate = function(o){
13476         return '"' + o.getFullYear() + "-" +
13477                 pad(o.getMonth() + 1) + "-" +
13478                 pad(o.getDate()) + "T" +
13479                 pad(o.getHours()) + ":" +
13480                 pad(o.getMinutes()) + ":" +
13481                 pad(o.getSeconds()) + '"';
13482     };
13483     
13484     /**
13485      * Encodes an Object, Array or other value
13486      * @param {Mixed} o The variable to encode
13487      * @return {String} The JSON string
13488      */
13489     this.encode = function(o)
13490     {
13491         // should this be extended to fully wrap stringify..
13492         
13493         if(typeof o == "undefined" || o === null){
13494             return "null";
13495         }else if(o instanceof Array){
13496             return encodeArray(o);
13497         }else if(o instanceof Date){
13498             return encodeDate(o);
13499         }else if(typeof o == "string"){
13500             return encodeString(o);
13501         }else if(typeof o == "number"){
13502             return isFinite(o) ? String(o) : "null";
13503         }else if(typeof o == "boolean"){
13504             return String(o);
13505         }else {
13506             var a = ["{"], b, i, v;
13507             for (i in o) {
13508                 if(!useHasOwn || o.hasOwnProperty(i)) {
13509                     v = o[i];
13510                     switch (typeof v) {
13511                     case "undefined":
13512                     case "function":
13513                     case "unknown":
13514                         break;
13515                     default:
13516                         if(b){
13517                             a.push(',');
13518                         }
13519                         a.push(this.encode(i), ":",
13520                                 v === null ? "null" : this.encode(v));
13521                         b = true;
13522                     }
13523                 }
13524             }
13525             a.push("}");
13526             return a.join("");
13527         }
13528     };
13529     
13530     /**
13531      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13532      * @param {String} json The JSON string
13533      * @return {Object} The resulting object
13534      */
13535     this.decode = function(json){
13536         
13537         return  /** eval:var:json */ eval("(" + json + ')');
13538     };
13539 })();
13540 /** 
13541  * Shorthand for {@link Roo.util.JSON#encode}
13542  * @member Roo encode 
13543  * @method */
13544 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13545 /** 
13546  * Shorthand for {@link Roo.util.JSON#decode}
13547  * @member Roo decode 
13548  * @method */
13549 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13550 /*
13551  * Based on:
13552  * Ext JS Library 1.1.1
13553  * Copyright(c) 2006-2007, Ext JS, LLC.
13554  *
13555  * Originally Released Under LGPL - original licence link has changed is not relivant.
13556  *
13557  * Fork - LGPL
13558  * <script type="text/javascript">
13559  */
13560  
13561 /**
13562  * @class Roo.util.Format
13563  * Reusable data formatting functions
13564  * @singleton
13565  */
13566 Roo.util.Format = function(){
13567     var trimRe = /^\s+|\s+$/g;
13568     return {
13569         /**
13570          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13571          * @param {String} value The string to truncate
13572          * @param {Number} length The maximum length to allow before truncating
13573          * @return {String} The converted text
13574          */
13575         ellipsis : function(value, len){
13576             if(value && value.length > len){
13577                 return value.substr(0, len-3)+"...";
13578             }
13579             return value;
13580         },
13581
13582         /**
13583          * Checks a reference and converts it to empty string if it is undefined
13584          * @param {Mixed} value Reference to check
13585          * @return {Mixed} Empty string if converted, otherwise the original value
13586          */
13587         undef : function(value){
13588             return typeof value != "undefined" ? value : "";
13589         },
13590
13591         /**
13592          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13593          * @param {String} value The string to encode
13594          * @return {String} The encoded text
13595          */
13596         htmlEncode : function(value){
13597             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13598         },
13599
13600         /**
13601          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13602          * @param {String} value The string to decode
13603          * @return {String} The decoded text
13604          */
13605         htmlDecode : function(value){
13606             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13607         },
13608
13609         /**
13610          * Trims any whitespace from either side of a string
13611          * @param {String} value The text to trim
13612          * @return {String} The trimmed text
13613          */
13614         trim : function(value){
13615             return String(value).replace(trimRe, "");
13616         },
13617
13618         /**
13619          * Returns a substring from within an original string
13620          * @param {String} value The original text
13621          * @param {Number} start The start index of the substring
13622          * @param {Number} length The length of the substring
13623          * @return {String} The substring
13624          */
13625         substr : function(value, start, length){
13626             return String(value).substr(start, length);
13627         },
13628
13629         /**
13630          * Converts a string to all lower case letters
13631          * @param {String} value The text to convert
13632          * @return {String} The converted text
13633          */
13634         lowercase : function(value){
13635             return String(value).toLowerCase();
13636         },
13637
13638         /**
13639          * Converts a string to all upper case letters
13640          * @param {String} value The text to convert
13641          * @return {String} The converted text
13642          */
13643         uppercase : function(value){
13644             return String(value).toUpperCase();
13645         },
13646
13647         /**
13648          * Converts the first character only of a string to upper case
13649          * @param {String} value The text to convert
13650          * @return {String} The converted text
13651          */
13652         capitalize : function(value){
13653             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13654         },
13655
13656         // private
13657         call : function(value, fn){
13658             if(arguments.length > 2){
13659                 var args = Array.prototype.slice.call(arguments, 2);
13660                 args.unshift(value);
13661                  
13662                 return /** eval:var:value */  eval(fn).apply(window, args);
13663             }else{
13664                 /** eval:var:value */
13665                 return /** eval:var:value */ eval(fn).call(window, value);
13666             }
13667         },
13668
13669        
13670         /**
13671          * safer version of Math.toFixed..??/
13672          * @param {Number/String} value The numeric value to format
13673          * @param {Number/String} value Decimal places 
13674          * @return {String} The formatted currency string
13675          */
13676         toFixed : function(v, n)
13677         {
13678             // why not use to fixed - precision is buggered???
13679             if (!n) {
13680                 return Math.round(v-0);
13681             }
13682             var fact = Math.pow(10,n+1);
13683             v = (Math.round((v-0)*fact))/fact;
13684             var z = (''+fact).substring(2);
13685             if (v == Math.floor(v)) {
13686                 return Math.floor(v) + '.' + z;
13687             }
13688             
13689             // now just padd decimals..
13690             var ps = String(v).split('.');
13691             var fd = (ps[1] + z);
13692             var r = fd.substring(0,n); 
13693             var rm = fd.substring(n); 
13694             if (rm < 5) {
13695                 return ps[0] + '.' + r;
13696             }
13697             r*=1; // turn it into a number;
13698             r++;
13699             if (String(r).length != n) {
13700                 ps[0]*=1;
13701                 ps[0]++;
13702                 r = String(r).substring(1); // chop the end off.
13703             }
13704             
13705             return ps[0] + '.' + r;
13706              
13707         },
13708         
13709         /**
13710          * Format a number as US currency
13711          * @param {Number/String} value The numeric value to format
13712          * @return {String} The formatted currency string
13713          */
13714         usMoney : function(v){
13715             return '$' + Roo.util.Format.number(v);
13716         },
13717         
13718         /**
13719          * Format a number
13720          * eventually this should probably emulate php's number_format
13721          * @param {Number/String} value The numeric value to format
13722          * @param {Number} decimals number of decimal places
13723          * @return {String} The formatted currency string
13724          */
13725         number : function(v,decimals)
13726         {
13727             // multiply and round.
13728             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13729             var mul = Math.pow(10, decimals);
13730             var zero = String(mul).substring(1);
13731             v = (Math.round((v-0)*mul))/mul;
13732             
13733             // if it's '0' number.. then
13734             
13735             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13736             v = String(v);
13737             var ps = v.split('.');
13738             var whole = ps[0];
13739             
13740             
13741             var r = /(\d+)(\d{3})/;
13742             // add comma's
13743             while (r.test(whole)) {
13744                 whole = whole.replace(r, '$1' + ',' + '$2');
13745             }
13746             
13747             
13748             var sub = ps[1] ?
13749                     // has decimals..
13750                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13751                     // does not have decimals
13752                     (decimals ? ('.' + zero) : '');
13753             
13754             
13755             return whole + sub ;
13756         },
13757         
13758         /**
13759          * Parse a value into a formatted date using the specified format pattern.
13760          * @param {Mixed} value The value to format
13761          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13762          * @return {String} The formatted date string
13763          */
13764         date : function(v, format){
13765             if(!v){
13766                 return "";
13767             }
13768             if(!(v instanceof Date)){
13769                 v = new Date(Date.parse(v));
13770             }
13771             return v.dateFormat(format || Roo.util.Format.defaults.date);
13772         },
13773
13774         /**
13775          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13776          * @param {String} format Any valid date format string
13777          * @return {Function} The date formatting function
13778          */
13779         dateRenderer : function(format){
13780             return function(v){
13781                 return Roo.util.Format.date(v, format);  
13782             };
13783         },
13784
13785         // private
13786         stripTagsRE : /<\/?[^>]+>/gi,
13787         
13788         /**
13789          * Strips all HTML tags
13790          * @param {Mixed} value The text from which to strip tags
13791          * @return {String} The stripped text
13792          */
13793         stripTags : function(v){
13794             return !v ? v : String(v).replace(this.stripTagsRE, "");
13795         }
13796     };
13797 }();
13798 Roo.util.Format.defaults = {
13799     date : 'd/M/Y'
13800 };/*
13801  * Based on:
13802  * Ext JS Library 1.1.1
13803  * Copyright(c) 2006-2007, Ext JS, LLC.
13804  *
13805  * Originally Released Under LGPL - original licence link has changed is not relivant.
13806  *
13807  * Fork - LGPL
13808  * <script type="text/javascript">
13809  */
13810
13811
13812  
13813
13814 /**
13815  * @class Roo.MasterTemplate
13816  * @extends Roo.Template
13817  * Provides a template that can have child templates. The syntax is:
13818 <pre><code>
13819 var t = new Roo.MasterTemplate(
13820         '&lt;select name="{name}"&gt;',
13821                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13822         '&lt;/select&gt;'
13823 );
13824 t.add('options', {value: 'foo', text: 'bar'});
13825 // or you can add multiple child elements in one shot
13826 t.addAll('options', [
13827     {value: 'foo', text: 'bar'},
13828     {value: 'foo2', text: 'bar2'},
13829     {value: 'foo3', text: 'bar3'}
13830 ]);
13831 // then append, applying the master template values
13832 t.append('my-form', {name: 'my-select'});
13833 </code></pre>
13834 * A name attribute for the child template is not required if you have only one child
13835 * template or you want to refer to them by index.
13836  */
13837 Roo.MasterTemplate = function(){
13838     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13839     this.originalHtml = this.html;
13840     var st = {};
13841     var m, re = this.subTemplateRe;
13842     re.lastIndex = 0;
13843     var subIndex = 0;
13844     while(m = re.exec(this.html)){
13845         var name = m[1], content = m[2];
13846         st[subIndex] = {
13847             name: name,
13848             index: subIndex,
13849             buffer: [],
13850             tpl : new Roo.Template(content)
13851         };
13852         if(name){
13853             st[name] = st[subIndex];
13854         }
13855         st[subIndex].tpl.compile();
13856         st[subIndex].tpl.call = this.call.createDelegate(this);
13857         subIndex++;
13858     }
13859     this.subCount = subIndex;
13860     this.subs = st;
13861 };
13862 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13863     /**
13864     * The regular expression used to match sub templates
13865     * @type RegExp
13866     * @property
13867     */
13868     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13869
13870     /**
13871      * Applies the passed values to a child template.
13872      * @param {String/Number} name (optional) The name or index of the child template
13873      * @param {Array/Object} values The values to be applied to the template
13874      * @return {MasterTemplate} this
13875      */
13876      add : function(name, values){
13877         if(arguments.length == 1){
13878             values = arguments[0];
13879             name = 0;
13880         }
13881         var s = this.subs[name];
13882         s.buffer[s.buffer.length] = s.tpl.apply(values);
13883         return this;
13884     },
13885
13886     /**
13887      * Applies all the passed values to a child template.
13888      * @param {String/Number} name (optional) The name or index of the child template
13889      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13890      * @param {Boolean} reset (optional) True to reset the template first
13891      * @return {MasterTemplate} this
13892      */
13893     fill : function(name, values, reset){
13894         var a = arguments;
13895         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13896             values = a[0];
13897             name = 0;
13898             reset = a[1];
13899         }
13900         if(reset){
13901             this.reset();
13902         }
13903         for(var i = 0, len = values.length; i < len; i++){
13904             this.add(name, values[i]);
13905         }
13906         return this;
13907     },
13908
13909     /**
13910      * Resets the template for reuse
13911      * @return {MasterTemplate} this
13912      */
13913      reset : function(){
13914         var s = this.subs;
13915         for(var i = 0; i < this.subCount; i++){
13916             s[i].buffer = [];
13917         }
13918         return this;
13919     },
13920
13921     applyTemplate : function(values){
13922         var s = this.subs;
13923         var replaceIndex = -1;
13924         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13925             return s[++replaceIndex].buffer.join("");
13926         });
13927         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13928     },
13929
13930     apply : function(){
13931         return this.applyTemplate.apply(this, arguments);
13932     },
13933
13934     compile : function(){return this;}
13935 });
13936
13937 /**
13938  * Alias for fill().
13939  * @method
13940  */
13941 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13942  /**
13943  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13944  * var tpl = Roo.MasterTemplate.from('element-id');
13945  * @param {String/HTMLElement} el
13946  * @param {Object} config
13947  * @static
13948  */
13949 Roo.MasterTemplate.from = function(el, config){
13950     el = Roo.getDom(el);
13951     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13952 };/*
13953  * Based on:
13954  * Ext JS Library 1.1.1
13955  * Copyright(c) 2006-2007, Ext JS, LLC.
13956  *
13957  * Originally Released Under LGPL - original licence link has changed is not relivant.
13958  *
13959  * Fork - LGPL
13960  * <script type="text/javascript">
13961  */
13962
13963  
13964 /**
13965  * @class Roo.util.CSS
13966  * Utility class for manipulating CSS rules
13967  * @singleton
13968  */
13969 Roo.util.CSS = function(){
13970         var rules = null;
13971         var doc = document;
13972
13973     var camelRe = /(-[a-z])/gi;
13974     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13975
13976    return {
13977    /**
13978     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13979     * tag and appended to the HEAD of the document.
13980     * @param {String|Object} cssText The text containing the css rules
13981     * @param {String} id An id to add to the stylesheet for later removal
13982     * @return {StyleSheet}
13983     */
13984     createStyleSheet : function(cssText, id){
13985         var ss;
13986         var head = doc.getElementsByTagName("head")[0];
13987         var nrules = doc.createElement("style");
13988         nrules.setAttribute("type", "text/css");
13989         if(id){
13990             nrules.setAttribute("id", id);
13991         }
13992         if (typeof(cssText) != 'string') {
13993             // support object maps..
13994             // not sure if this a good idea.. 
13995             // perhaps it should be merged with the general css handling
13996             // and handle js style props.
13997             var cssTextNew = [];
13998             for(var n in cssText) {
13999                 var citems = [];
14000                 for(var k in cssText[n]) {
14001                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14002                 }
14003                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14004                 
14005             }
14006             cssText = cssTextNew.join("\n");
14007             
14008         }
14009        
14010        
14011        if(Roo.isIE){
14012            head.appendChild(nrules);
14013            ss = nrules.styleSheet;
14014            ss.cssText = cssText;
14015        }else{
14016            try{
14017                 nrules.appendChild(doc.createTextNode(cssText));
14018            }catch(e){
14019                nrules.cssText = cssText; 
14020            }
14021            head.appendChild(nrules);
14022            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14023        }
14024        this.cacheStyleSheet(ss);
14025        return ss;
14026    },
14027
14028    /**
14029     * Removes a style or link tag by id
14030     * @param {String} id The id of the tag
14031     */
14032    removeStyleSheet : function(id){
14033        var existing = doc.getElementById(id);
14034        if(existing){
14035            existing.parentNode.removeChild(existing);
14036        }
14037    },
14038
14039    /**
14040     * Dynamically swaps an existing stylesheet reference for a new one
14041     * @param {String} id The id of an existing link tag to remove
14042     * @param {String} url The href of the new stylesheet to include
14043     */
14044    swapStyleSheet : function(id, url){
14045        this.removeStyleSheet(id);
14046        var ss = doc.createElement("link");
14047        ss.setAttribute("rel", "stylesheet");
14048        ss.setAttribute("type", "text/css");
14049        ss.setAttribute("id", id);
14050        ss.setAttribute("href", url);
14051        doc.getElementsByTagName("head")[0].appendChild(ss);
14052    },
14053    
14054    /**
14055     * Refresh the rule cache if you have dynamically added stylesheets
14056     * @return {Object} An object (hash) of rules indexed by selector
14057     */
14058    refreshCache : function(){
14059        return this.getRules(true);
14060    },
14061
14062    // private
14063    cacheStyleSheet : function(stylesheet){
14064        if(!rules){
14065            rules = {};
14066        }
14067        try{// try catch for cross domain access issue
14068            var ssRules = stylesheet.cssRules || stylesheet.rules;
14069            for(var j = ssRules.length-1; j >= 0; --j){
14070                rules[ssRules[j].selectorText] = ssRules[j];
14071            }
14072        }catch(e){}
14073    },
14074    
14075    /**
14076     * Gets all css rules for the document
14077     * @param {Boolean} refreshCache true to refresh the internal cache
14078     * @return {Object} An object (hash) of rules indexed by selector
14079     */
14080    getRules : function(refreshCache){
14081                 if(rules == null || refreshCache){
14082                         rules = {};
14083                         var ds = doc.styleSheets;
14084                         for(var i =0, len = ds.length; i < len; i++){
14085                             try{
14086                         this.cacheStyleSheet(ds[i]);
14087                     }catch(e){} 
14088                 }
14089                 }
14090                 return rules;
14091         },
14092         
14093         /**
14094     * Gets an an individual CSS rule by selector(s)
14095     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14096     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14097     * @return {CSSRule} The CSS rule or null if one is not found
14098     */
14099    getRule : function(selector, refreshCache){
14100                 var rs = this.getRules(refreshCache);
14101                 if(!(selector instanceof Array)){
14102                     return rs[selector];
14103                 }
14104                 for(var i = 0; i < selector.length; i++){
14105                         if(rs[selector[i]]){
14106                                 return rs[selector[i]];
14107                         }
14108                 }
14109                 return null;
14110         },
14111         
14112         
14113         /**
14114     * Updates a rule property
14115     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14116     * @param {String} property The css property
14117     * @param {String} value The new value for the property
14118     * @return {Boolean} true If a rule was found and updated
14119     */
14120    updateRule : function(selector, property, value){
14121                 if(!(selector instanceof Array)){
14122                         var rule = this.getRule(selector);
14123                         if(rule){
14124                                 rule.style[property.replace(camelRe, camelFn)] = value;
14125                                 return true;
14126                         }
14127                 }else{
14128                         for(var i = 0; i < selector.length; i++){
14129                                 if(this.updateRule(selector[i], property, value)){
14130                                         return true;
14131                                 }
14132                         }
14133                 }
14134                 return false;
14135         }
14136    };   
14137 }();/*
14138  * Based on:
14139  * Ext JS Library 1.1.1
14140  * Copyright(c) 2006-2007, Ext JS, LLC.
14141  *
14142  * Originally Released Under LGPL - original licence link has changed is not relivant.
14143  *
14144  * Fork - LGPL
14145  * <script type="text/javascript">
14146  */
14147
14148  
14149
14150 /**
14151  * @class Roo.util.ClickRepeater
14152  * @extends Roo.util.Observable
14153  * 
14154  * A wrapper class which can be applied to any element. Fires a "click" event while the
14155  * mouse is pressed. The interval between firings may be specified in the config but
14156  * defaults to 10 milliseconds.
14157  * 
14158  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14159  * 
14160  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14161  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14162  * Similar to an autorepeat key delay.
14163  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14164  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14165  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14166  *           "interval" and "delay" are ignored. "immediate" is honored.
14167  * @cfg {Boolean} preventDefault True to prevent the default click event
14168  * @cfg {Boolean} stopDefault True to stop the default click event
14169  * 
14170  * @history
14171  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14172  *     2007-02-02 jvs Renamed to ClickRepeater
14173  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14174  *
14175  *  @constructor
14176  * @param {String/HTMLElement/Element} el The element to listen on
14177  * @param {Object} config
14178  **/
14179 Roo.util.ClickRepeater = function(el, config)
14180 {
14181     this.el = Roo.get(el);
14182     this.el.unselectable();
14183
14184     Roo.apply(this, config);
14185
14186     this.addEvents({
14187     /**
14188      * @event mousedown
14189      * Fires when the mouse button is depressed.
14190      * @param {Roo.util.ClickRepeater} this
14191      */
14192         "mousedown" : true,
14193     /**
14194      * @event click
14195      * Fires on a specified interval during the time the element is pressed.
14196      * @param {Roo.util.ClickRepeater} this
14197      */
14198         "click" : true,
14199     /**
14200      * @event mouseup
14201      * Fires when the mouse key is released.
14202      * @param {Roo.util.ClickRepeater} this
14203      */
14204         "mouseup" : true
14205     });
14206
14207     this.el.on("mousedown", this.handleMouseDown, this);
14208     if(this.preventDefault || this.stopDefault){
14209         this.el.on("click", function(e){
14210             if(this.preventDefault){
14211                 e.preventDefault();
14212             }
14213             if(this.stopDefault){
14214                 e.stopEvent();
14215             }
14216         }, this);
14217     }
14218
14219     // allow inline handler
14220     if(this.handler){
14221         this.on("click", this.handler,  this.scope || this);
14222     }
14223
14224     Roo.util.ClickRepeater.superclass.constructor.call(this);
14225 };
14226
14227 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14228     interval : 20,
14229     delay: 250,
14230     preventDefault : true,
14231     stopDefault : false,
14232     timer : 0,
14233
14234     // private
14235     handleMouseDown : function(){
14236         clearTimeout(this.timer);
14237         this.el.blur();
14238         if(this.pressClass){
14239             this.el.addClass(this.pressClass);
14240         }
14241         this.mousedownTime = new Date();
14242
14243         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14244         this.el.on("mouseout", this.handleMouseOut, this);
14245
14246         this.fireEvent("mousedown", this);
14247         this.fireEvent("click", this);
14248         
14249         this.timer = this.click.defer(this.delay || this.interval, this);
14250     },
14251
14252     // private
14253     click : function(){
14254         this.fireEvent("click", this);
14255         this.timer = this.click.defer(this.getInterval(), this);
14256     },
14257
14258     // private
14259     getInterval: function(){
14260         if(!this.accelerate){
14261             return this.interval;
14262         }
14263         var pressTime = this.mousedownTime.getElapsed();
14264         if(pressTime < 500){
14265             return 400;
14266         }else if(pressTime < 1700){
14267             return 320;
14268         }else if(pressTime < 2600){
14269             return 250;
14270         }else if(pressTime < 3500){
14271             return 180;
14272         }else if(pressTime < 4400){
14273             return 140;
14274         }else if(pressTime < 5300){
14275             return 80;
14276         }else if(pressTime < 6200){
14277             return 50;
14278         }else{
14279             return 10;
14280         }
14281     },
14282
14283     // private
14284     handleMouseOut : function(){
14285         clearTimeout(this.timer);
14286         if(this.pressClass){
14287             this.el.removeClass(this.pressClass);
14288         }
14289         this.el.on("mouseover", this.handleMouseReturn, this);
14290     },
14291
14292     // private
14293     handleMouseReturn : function(){
14294         this.el.un("mouseover", this.handleMouseReturn);
14295         if(this.pressClass){
14296             this.el.addClass(this.pressClass);
14297         }
14298         this.click();
14299     },
14300
14301     // private
14302     handleMouseUp : function(){
14303         clearTimeout(this.timer);
14304         this.el.un("mouseover", this.handleMouseReturn);
14305         this.el.un("mouseout", this.handleMouseOut);
14306         Roo.get(document).un("mouseup", this.handleMouseUp);
14307         this.el.removeClass(this.pressClass);
14308         this.fireEvent("mouseup", this);
14309     }
14310 });/*
14311  * Based on:
14312  * Ext JS Library 1.1.1
14313  * Copyright(c) 2006-2007, Ext JS, LLC.
14314  *
14315  * Originally Released Under LGPL - original licence link has changed is not relivant.
14316  *
14317  * Fork - LGPL
14318  * <script type="text/javascript">
14319  */
14320
14321  
14322 /**
14323  * @class Roo.KeyNav
14324  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14325  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14326  * way to implement custom navigation schemes for any UI component.</p>
14327  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14328  * pageUp, pageDown, del, home, end.  Usage:</p>
14329  <pre><code>
14330 var nav = new Roo.KeyNav("my-element", {
14331     "left" : function(e){
14332         this.moveLeft(e.ctrlKey);
14333     },
14334     "right" : function(e){
14335         this.moveRight(e.ctrlKey);
14336     },
14337     "enter" : function(e){
14338         this.save();
14339     },
14340     scope : this
14341 });
14342 </code></pre>
14343  * @constructor
14344  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14345  * @param {Object} config The config
14346  */
14347 Roo.KeyNav = function(el, config){
14348     this.el = Roo.get(el);
14349     Roo.apply(this, config);
14350     if(!this.disabled){
14351         this.disabled = true;
14352         this.enable();
14353     }
14354 };
14355
14356 Roo.KeyNav.prototype = {
14357     /**
14358      * @cfg {Boolean} disabled
14359      * True to disable this KeyNav instance (defaults to false)
14360      */
14361     disabled : false,
14362     /**
14363      * @cfg {String} defaultEventAction
14364      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14365      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14366      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14367      */
14368     defaultEventAction: "stopEvent",
14369     /**
14370      * @cfg {Boolean} forceKeyDown
14371      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14372      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14373      * handle keydown instead of keypress.
14374      */
14375     forceKeyDown : false,
14376
14377     // private
14378     prepareEvent : function(e){
14379         var k = e.getKey();
14380         var h = this.keyToHandler[k];
14381         //if(h && this[h]){
14382         //    e.stopPropagation();
14383         //}
14384         if(Roo.isSafari && h && k >= 37 && k <= 40){
14385             e.stopEvent();
14386         }
14387     },
14388
14389     // private
14390     relay : function(e){
14391         var k = e.getKey();
14392         var h = this.keyToHandler[k];
14393         if(h && this[h]){
14394             if(this.doRelay(e, this[h], h) !== true){
14395                 e[this.defaultEventAction]();
14396             }
14397         }
14398     },
14399
14400     // private
14401     doRelay : function(e, h, hname){
14402         return h.call(this.scope || this, e);
14403     },
14404
14405     // possible handlers
14406     enter : false,
14407     left : false,
14408     right : false,
14409     up : false,
14410     down : false,
14411     tab : false,
14412     esc : false,
14413     pageUp : false,
14414     pageDown : false,
14415     del : false,
14416     home : false,
14417     end : false,
14418
14419     // quick lookup hash
14420     keyToHandler : {
14421         37 : "left",
14422         39 : "right",
14423         38 : "up",
14424         40 : "down",
14425         33 : "pageUp",
14426         34 : "pageDown",
14427         46 : "del",
14428         36 : "home",
14429         35 : "end",
14430         13 : "enter",
14431         27 : "esc",
14432         9  : "tab"
14433     },
14434
14435         /**
14436          * Enable this KeyNav
14437          */
14438         enable: function(){
14439                 if(this.disabled){
14440             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14441             // the EventObject will normalize Safari automatically
14442             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14443                 this.el.on("keydown", this.relay,  this);
14444             }else{
14445                 this.el.on("keydown", this.prepareEvent,  this);
14446                 this.el.on("keypress", this.relay,  this);
14447             }
14448                     this.disabled = false;
14449                 }
14450         },
14451
14452         /**
14453          * Disable this KeyNav
14454          */
14455         disable: function(){
14456                 if(!this.disabled){
14457                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14458                 this.el.un("keydown", this.relay);
14459             }else{
14460                 this.el.un("keydown", this.prepareEvent);
14461                 this.el.un("keypress", this.relay);
14462             }
14463                     this.disabled = true;
14464                 }
14465         }
14466 };/*
14467  * Based on:
14468  * Ext JS Library 1.1.1
14469  * Copyright(c) 2006-2007, Ext JS, LLC.
14470  *
14471  * Originally Released Under LGPL - original licence link has changed is not relivant.
14472  *
14473  * Fork - LGPL
14474  * <script type="text/javascript">
14475  */
14476
14477  
14478 /**
14479  * @class Roo.KeyMap
14480  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14481  * The constructor accepts the same config object as defined by {@link #addBinding}.
14482  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14483  * combination it will call the function with this signature (if the match is a multi-key
14484  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14485  * A KeyMap can also handle a string representation of keys.<br />
14486  * Usage:
14487  <pre><code>
14488 // map one key by key code
14489 var map = new Roo.KeyMap("my-element", {
14490     key: 13, // or Roo.EventObject.ENTER
14491     fn: myHandler,
14492     scope: myObject
14493 });
14494
14495 // map multiple keys to one action by string
14496 var map = new Roo.KeyMap("my-element", {
14497     key: "a\r\n\t",
14498     fn: myHandler,
14499     scope: myObject
14500 });
14501
14502 // map multiple keys to multiple actions by strings and array of codes
14503 var map = new Roo.KeyMap("my-element", [
14504     {
14505         key: [10,13],
14506         fn: function(){ alert("Return was pressed"); }
14507     }, {
14508         key: "abc",
14509         fn: function(){ alert('a, b or c was pressed'); }
14510     }, {
14511         key: "\t",
14512         ctrl:true,
14513         shift:true,
14514         fn: function(){ alert('Control + shift + tab was pressed.'); }
14515     }
14516 ]);
14517 </code></pre>
14518  * <b>Note: A KeyMap starts enabled</b>
14519  * @constructor
14520  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14521  * @param {Object} config The config (see {@link #addBinding})
14522  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14523  */
14524 Roo.KeyMap = function(el, config, eventName){
14525     this.el  = Roo.get(el);
14526     this.eventName = eventName || "keydown";
14527     this.bindings = [];
14528     if(config){
14529         this.addBinding(config);
14530     }
14531     this.enable();
14532 };
14533
14534 Roo.KeyMap.prototype = {
14535     /**
14536      * True to stop the event from bubbling and prevent the default browser action if the
14537      * key was handled by the KeyMap (defaults to false)
14538      * @type Boolean
14539      */
14540     stopEvent : false,
14541
14542     /**
14543      * Add a new binding to this KeyMap. The following config object properties are supported:
14544      * <pre>
14545 Property    Type             Description
14546 ----------  ---------------  ----------------------------------------------------------------------
14547 key         String/Array     A single keycode or an array of keycodes to handle
14548 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14549 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14550 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14551 fn          Function         The function to call when KeyMap finds the expected key combination
14552 scope       Object           The scope of the callback function
14553 </pre>
14554      *
14555      * Usage:
14556      * <pre><code>
14557 // Create a KeyMap
14558 var map = new Roo.KeyMap(document, {
14559     key: Roo.EventObject.ENTER,
14560     fn: handleKey,
14561     scope: this
14562 });
14563
14564 //Add a new binding to the existing KeyMap later
14565 map.addBinding({
14566     key: 'abc',
14567     shift: true,
14568     fn: handleKey,
14569     scope: this
14570 });
14571 </code></pre>
14572      * @param {Object/Array} config A single KeyMap config or an array of configs
14573      */
14574         addBinding : function(config){
14575         if(config instanceof Array){
14576             for(var i = 0, len = config.length; i < len; i++){
14577                 this.addBinding(config[i]);
14578             }
14579             return;
14580         }
14581         var keyCode = config.key,
14582             shift = config.shift, 
14583             ctrl = config.ctrl, 
14584             alt = config.alt,
14585             fn = config.fn,
14586             scope = config.scope;
14587         if(typeof keyCode == "string"){
14588             var ks = [];
14589             var keyString = keyCode.toUpperCase();
14590             for(var j = 0, len = keyString.length; j < len; j++){
14591                 ks.push(keyString.charCodeAt(j));
14592             }
14593             keyCode = ks;
14594         }
14595         var keyArray = keyCode instanceof Array;
14596         var handler = function(e){
14597             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14598                 var k = e.getKey();
14599                 if(keyArray){
14600                     for(var i = 0, len = keyCode.length; i < len; i++){
14601                         if(keyCode[i] == k){
14602                           if(this.stopEvent){
14603                               e.stopEvent();
14604                           }
14605                           fn.call(scope || window, k, e);
14606                           return;
14607                         }
14608                     }
14609                 }else{
14610                     if(k == keyCode){
14611                         if(this.stopEvent){
14612                            e.stopEvent();
14613                         }
14614                         fn.call(scope || window, k, e);
14615                     }
14616                 }
14617             }
14618         };
14619         this.bindings.push(handler);  
14620         },
14621
14622     /**
14623      * Shorthand for adding a single key listener
14624      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14625      * following options:
14626      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14627      * @param {Function} fn The function to call
14628      * @param {Object} scope (optional) The scope of the function
14629      */
14630     on : function(key, fn, scope){
14631         var keyCode, shift, ctrl, alt;
14632         if(typeof key == "object" && !(key instanceof Array)){
14633             keyCode = key.key;
14634             shift = key.shift;
14635             ctrl = key.ctrl;
14636             alt = key.alt;
14637         }else{
14638             keyCode = key;
14639         }
14640         this.addBinding({
14641             key: keyCode,
14642             shift: shift,
14643             ctrl: ctrl,
14644             alt: alt,
14645             fn: fn,
14646             scope: scope
14647         })
14648     },
14649
14650     // private
14651     handleKeyDown : function(e){
14652             if(this.enabled){ //just in case
14653             var b = this.bindings;
14654             for(var i = 0, len = b.length; i < len; i++){
14655                 b[i].call(this, e);
14656             }
14657             }
14658         },
14659         
14660         /**
14661          * Returns true if this KeyMap is enabled
14662          * @return {Boolean} 
14663          */
14664         isEnabled : function(){
14665             return this.enabled;  
14666         },
14667         
14668         /**
14669          * Enables this KeyMap
14670          */
14671         enable: function(){
14672                 if(!this.enabled){
14673                     this.el.on(this.eventName, this.handleKeyDown, this);
14674                     this.enabled = true;
14675                 }
14676         },
14677
14678         /**
14679          * Disable this KeyMap
14680          */
14681         disable: function(){
14682                 if(this.enabled){
14683                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14684                     this.enabled = false;
14685                 }
14686         }
14687 };/*
14688  * Based on:
14689  * Ext JS Library 1.1.1
14690  * Copyright(c) 2006-2007, Ext JS, LLC.
14691  *
14692  * Originally Released Under LGPL - original licence link has changed is not relivant.
14693  *
14694  * Fork - LGPL
14695  * <script type="text/javascript">
14696  */
14697
14698  
14699 /**
14700  * @class Roo.util.TextMetrics
14701  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14702  * wide, in pixels, a given block of text will be.
14703  * @singleton
14704  */
14705 Roo.util.TextMetrics = function(){
14706     var shared;
14707     return {
14708         /**
14709          * Measures the size of the specified text
14710          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14711          * that can affect the size of the rendered text
14712          * @param {String} text The text to measure
14713          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14714          * in order to accurately measure the text height
14715          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14716          */
14717         measure : function(el, text, fixedWidth){
14718             if(!shared){
14719                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14720             }
14721             shared.bind(el);
14722             shared.setFixedWidth(fixedWidth || 'auto');
14723             return shared.getSize(text);
14724         },
14725
14726         /**
14727          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14728          * the overhead of multiple calls to initialize the style properties on each measurement.
14729          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14730          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14731          * in order to accurately measure the text height
14732          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14733          */
14734         createInstance : function(el, fixedWidth){
14735             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14736         }
14737     };
14738 }();
14739
14740  
14741
14742 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14743     var ml = new Roo.Element(document.createElement('div'));
14744     document.body.appendChild(ml.dom);
14745     ml.position('absolute');
14746     ml.setLeftTop(-1000, -1000);
14747     ml.hide();
14748
14749     if(fixedWidth){
14750         ml.setWidth(fixedWidth);
14751     }
14752      
14753     var instance = {
14754         /**
14755          * Returns the size of the specified text based on the internal element's style and width properties
14756          * @memberOf Roo.util.TextMetrics.Instance#
14757          * @param {String} text The text to measure
14758          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14759          */
14760         getSize : function(text){
14761             ml.update(text);
14762             var s = ml.getSize();
14763             ml.update('');
14764             return s;
14765         },
14766
14767         /**
14768          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14769          * that can affect the size of the rendered text
14770          * @memberOf Roo.util.TextMetrics.Instance#
14771          * @param {String/HTMLElement} el The element, dom node or id
14772          */
14773         bind : function(el){
14774             ml.setStyle(
14775                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14776             );
14777         },
14778
14779         /**
14780          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14781          * to set a fixed width in order to accurately measure the text height.
14782          * @memberOf Roo.util.TextMetrics.Instance#
14783          * @param {Number} width The width to set on the element
14784          */
14785         setFixedWidth : function(width){
14786             ml.setWidth(width);
14787         },
14788
14789         /**
14790          * Returns the measured width of the specified text
14791          * @memberOf Roo.util.TextMetrics.Instance#
14792          * @param {String} text The text to measure
14793          * @return {Number} width The width in pixels
14794          */
14795         getWidth : function(text){
14796             ml.dom.style.width = 'auto';
14797             return this.getSize(text).width;
14798         },
14799
14800         /**
14801          * Returns the measured height of the specified text.  For multiline text, be sure to call
14802          * {@link #setFixedWidth} if necessary.
14803          * @memberOf Roo.util.TextMetrics.Instance#
14804          * @param {String} text The text to measure
14805          * @return {Number} height The height in pixels
14806          */
14807         getHeight : function(text){
14808             return this.getSize(text).height;
14809         }
14810     };
14811
14812     instance.bind(bindTo);
14813
14814     return instance;
14815 };
14816
14817 // backwards compat
14818 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14819  * Based on:
14820  * Ext JS Library 1.1.1
14821  * Copyright(c) 2006-2007, Ext JS, LLC.
14822  *
14823  * Originally Released Under LGPL - original licence link has changed is not relivant.
14824  *
14825  * Fork - LGPL
14826  * <script type="text/javascript">
14827  */
14828
14829 /**
14830  * @class Roo.state.Provider
14831  * Abstract base class for state provider implementations. This class provides methods
14832  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14833  * Provider interface.
14834  */
14835 Roo.state.Provider = function(){
14836     /**
14837      * @event statechange
14838      * Fires when a state change occurs.
14839      * @param {Provider} this This state provider
14840      * @param {String} key The state key which was changed
14841      * @param {String} value The encoded value for the state
14842      */
14843     this.addEvents({
14844         "statechange": true
14845     });
14846     this.state = {};
14847     Roo.state.Provider.superclass.constructor.call(this);
14848 };
14849 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14850     /**
14851      * Returns the current value for a key
14852      * @param {String} name The key name
14853      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14854      * @return {Mixed} The state data
14855      */
14856     get : function(name, defaultValue){
14857         return typeof this.state[name] == "undefined" ?
14858             defaultValue : this.state[name];
14859     },
14860     
14861     /**
14862      * Clears a value from the state
14863      * @param {String} name The key name
14864      */
14865     clear : function(name){
14866         delete this.state[name];
14867         this.fireEvent("statechange", this, name, null);
14868     },
14869     
14870     /**
14871      * Sets the value for a key
14872      * @param {String} name The key name
14873      * @param {Mixed} value The value to set
14874      */
14875     set : function(name, value){
14876         this.state[name] = value;
14877         this.fireEvent("statechange", this, name, value);
14878     },
14879     
14880     /**
14881      * Decodes a string previously encoded with {@link #encodeValue}.
14882      * @param {String} value The value to decode
14883      * @return {Mixed} The decoded value
14884      */
14885     decodeValue : function(cookie){
14886         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14887         var matches = re.exec(unescape(cookie));
14888         if(!matches || !matches[1]) {
14889             return; // non state cookie
14890         }
14891         var type = matches[1];
14892         var v = matches[2];
14893         switch(type){
14894             case "n":
14895                 return parseFloat(v);
14896             case "d":
14897                 return new Date(Date.parse(v));
14898             case "b":
14899                 return (v == "1");
14900             case "a":
14901                 var all = [];
14902                 var values = v.split("^");
14903                 for(var i = 0, len = values.length; i < len; i++){
14904                     all.push(this.decodeValue(values[i]));
14905                 }
14906                 return all;
14907            case "o":
14908                 var all = {};
14909                 var values = v.split("^");
14910                 for(var i = 0, len = values.length; i < len; i++){
14911                     var kv = values[i].split("=");
14912                     all[kv[0]] = this.decodeValue(kv[1]);
14913                 }
14914                 return all;
14915            default:
14916                 return v;
14917         }
14918     },
14919     
14920     /**
14921      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14922      * @param {Mixed} value The value to encode
14923      * @return {String} The encoded value
14924      */
14925     encodeValue : function(v){
14926         var enc;
14927         if(typeof v == "number"){
14928             enc = "n:" + v;
14929         }else if(typeof v == "boolean"){
14930             enc = "b:" + (v ? "1" : "0");
14931         }else if(v instanceof Date){
14932             enc = "d:" + v.toGMTString();
14933         }else if(v instanceof Array){
14934             var flat = "";
14935             for(var i = 0, len = v.length; i < len; i++){
14936                 flat += this.encodeValue(v[i]);
14937                 if(i != len-1) {
14938                     flat += "^";
14939                 }
14940             }
14941             enc = "a:" + flat;
14942         }else if(typeof v == "object"){
14943             var flat = "";
14944             for(var key in v){
14945                 if(typeof v[key] != "function"){
14946                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14947                 }
14948             }
14949             enc = "o:" + flat.substring(0, flat.length-1);
14950         }else{
14951             enc = "s:" + v;
14952         }
14953         return escape(enc);        
14954     }
14955 });
14956
14957 /*
14958  * Based on:
14959  * Ext JS Library 1.1.1
14960  * Copyright(c) 2006-2007, Ext JS, LLC.
14961  *
14962  * Originally Released Under LGPL - original licence link has changed is not relivant.
14963  *
14964  * Fork - LGPL
14965  * <script type="text/javascript">
14966  */
14967 /**
14968  * @class Roo.state.Manager
14969  * This is the global state manager. By default all components that are "state aware" check this class
14970  * for state information if you don't pass them a custom state provider. In order for this class
14971  * to be useful, it must be initialized with a provider when your application initializes.
14972  <pre><code>
14973 // in your initialization function
14974 init : function(){
14975    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14976    ...
14977    // supposed you have a {@link Roo.BorderLayout}
14978    var layout = new Roo.BorderLayout(...);
14979    layout.restoreState();
14980    // or a {Roo.BasicDialog}
14981    var dialog = new Roo.BasicDialog(...);
14982    dialog.restoreState();
14983  </code></pre>
14984  * @singleton
14985  */
14986 Roo.state.Manager = function(){
14987     var provider = new Roo.state.Provider();
14988     
14989     return {
14990         /**
14991          * Configures the default state provider for your application
14992          * @param {Provider} stateProvider The state provider to set
14993          */
14994         setProvider : function(stateProvider){
14995             provider = stateProvider;
14996         },
14997         
14998         /**
14999          * Returns the current value for a key
15000          * @param {String} name The key name
15001          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15002          * @return {Mixed} The state data
15003          */
15004         get : function(key, defaultValue){
15005             return provider.get(key, defaultValue);
15006         },
15007         
15008         /**
15009          * Sets the value for a key
15010          * @param {String} name The key name
15011          * @param {Mixed} value The state data
15012          */
15013          set : function(key, value){
15014             provider.set(key, value);
15015         },
15016         
15017         /**
15018          * Clears a value from the state
15019          * @param {String} name The key name
15020          */
15021         clear : function(key){
15022             provider.clear(key);
15023         },
15024         
15025         /**
15026          * Gets the currently configured state provider
15027          * @return {Provider} The state provider
15028          */
15029         getProvider : function(){
15030             return provider;
15031         }
15032     };
15033 }();
15034 /*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044 /**
15045  * @class Roo.state.CookieProvider
15046  * @extends Roo.state.Provider
15047  * The default Provider implementation which saves state via cookies.
15048  * <br />Usage:
15049  <pre><code>
15050    var cp = new Roo.state.CookieProvider({
15051        path: "/cgi-bin/",
15052        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15053        domain: "roojs.com"
15054    })
15055    Roo.state.Manager.setProvider(cp);
15056  </code></pre>
15057  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15058  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15059  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15060  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15061  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15062  * domain the page is running on including the 'www' like 'www.roojs.com')
15063  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15064  * @constructor
15065  * Create a new CookieProvider
15066  * @param {Object} config The configuration object
15067  */
15068 Roo.state.CookieProvider = function(config){
15069     Roo.state.CookieProvider.superclass.constructor.call(this);
15070     this.path = "/";
15071     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15072     this.domain = null;
15073     this.secure = false;
15074     Roo.apply(this, config);
15075     this.state = this.readCookies();
15076 };
15077
15078 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15079     // private
15080     set : function(name, value){
15081         if(typeof value == "undefined" || value === null){
15082             this.clear(name);
15083             return;
15084         }
15085         this.setCookie(name, value);
15086         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15087     },
15088
15089     // private
15090     clear : function(name){
15091         this.clearCookie(name);
15092         Roo.state.CookieProvider.superclass.clear.call(this, name);
15093     },
15094
15095     // private
15096     readCookies : function(){
15097         var cookies = {};
15098         var c = document.cookie + ";";
15099         var re = /\s?(.*?)=(.*?);/g;
15100         var matches;
15101         while((matches = re.exec(c)) != null){
15102             var name = matches[1];
15103             var value = matches[2];
15104             if(name && name.substring(0,3) == "ys-"){
15105                 cookies[name.substr(3)] = this.decodeValue(value);
15106             }
15107         }
15108         return cookies;
15109     },
15110
15111     // private
15112     setCookie : function(name, value){
15113         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15114            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15115            ((this.path == null) ? "" : ("; path=" + this.path)) +
15116            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15117            ((this.secure == true) ? "; secure" : "");
15118     },
15119
15120     // private
15121     clearCookie : function(name){
15122         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15123            ((this.path == null) ? "" : ("; path=" + this.path)) +
15124            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15125            ((this.secure == true) ? "; secure" : "");
15126     }
15127 });/*
15128  * Based on:
15129  * Ext JS Library 1.1.1
15130  * Copyright(c) 2006-2007, Ext JS, LLC.
15131  *
15132  * Originally Released Under LGPL - original licence link has changed is not relivant.
15133  *
15134  * Fork - LGPL
15135  * <script type="text/javascript">
15136  */
15137  
15138
15139 /**
15140  * @class Roo.ComponentMgr
15141  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15142  * @singleton
15143  */
15144 Roo.ComponentMgr = function(){
15145     var all = new Roo.util.MixedCollection();
15146
15147     return {
15148         /**
15149          * Registers a component.
15150          * @param {Roo.Component} c The component
15151          */
15152         register : function(c){
15153             all.add(c);
15154         },
15155
15156         /**
15157          * Unregisters a component.
15158          * @param {Roo.Component} c The component
15159          */
15160         unregister : function(c){
15161             all.remove(c);
15162         },
15163
15164         /**
15165          * Returns a component by id
15166          * @param {String} id The component id
15167          */
15168         get : function(id){
15169             return all.get(id);
15170         },
15171
15172         /**
15173          * Registers a function that will be called when a specified component is added to ComponentMgr
15174          * @param {String} id The component id
15175          * @param {Funtction} fn The callback function
15176          * @param {Object} scope The scope of the callback
15177          */
15178         onAvailable : function(id, fn, scope){
15179             all.on("add", function(index, o){
15180                 if(o.id == id){
15181                     fn.call(scope || o, o);
15182                     all.un("add", fn, scope);
15183                 }
15184             });
15185         }
15186     };
15187 }();/*
15188  * Based on:
15189  * Ext JS Library 1.1.1
15190  * Copyright(c) 2006-2007, Ext JS, LLC.
15191  *
15192  * Originally Released Under LGPL - original licence link has changed is not relivant.
15193  *
15194  * Fork - LGPL
15195  * <script type="text/javascript">
15196  */
15197  
15198 /**
15199  * @class Roo.Component
15200  * @extends Roo.util.Observable
15201  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15202  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15203  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15204  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15205  * All visual components (widgets) that require rendering into a layout should subclass Component.
15206  * @constructor
15207  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15208  * 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
15209  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15210  */
15211 Roo.Component = function(config){
15212     config = config || {};
15213     if(config.tagName || config.dom || typeof config == "string"){ // element object
15214         config = {el: config, id: config.id || config};
15215     }
15216     this.initialConfig = config;
15217
15218     Roo.apply(this, config);
15219     this.addEvents({
15220         /**
15221          * @event disable
15222          * Fires after the component is disabled.
15223              * @param {Roo.Component} this
15224              */
15225         disable : true,
15226         /**
15227          * @event enable
15228          * Fires after the component is enabled.
15229              * @param {Roo.Component} this
15230              */
15231         enable : true,
15232         /**
15233          * @event beforeshow
15234          * Fires before the component is shown.  Return false to stop the show.
15235              * @param {Roo.Component} this
15236              */
15237         beforeshow : true,
15238         /**
15239          * @event show
15240          * Fires after the component is shown.
15241              * @param {Roo.Component} this
15242              */
15243         show : true,
15244         /**
15245          * @event beforehide
15246          * Fires before the component is hidden. Return false to stop the hide.
15247              * @param {Roo.Component} this
15248              */
15249         beforehide : true,
15250         /**
15251          * @event hide
15252          * Fires after the component is hidden.
15253              * @param {Roo.Component} this
15254              */
15255         hide : true,
15256         /**
15257          * @event beforerender
15258          * Fires before the component is rendered. Return false to stop the render.
15259              * @param {Roo.Component} this
15260              */
15261         beforerender : true,
15262         /**
15263          * @event render
15264          * Fires after the component is rendered.
15265              * @param {Roo.Component} this
15266              */
15267         render : true,
15268         /**
15269          * @event beforedestroy
15270          * Fires before the component is destroyed. Return false to stop the destroy.
15271              * @param {Roo.Component} this
15272              */
15273         beforedestroy : true,
15274         /**
15275          * @event destroy
15276          * Fires after the component is destroyed.
15277              * @param {Roo.Component} this
15278              */
15279         destroy : true
15280     });
15281     if(!this.id){
15282         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15283     }
15284     Roo.ComponentMgr.register(this);
15285     Roo.Component.superclass.constructor.call(this);
15286     this.initComponent();
15287     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15288         this.render(this.renderTo);
15289         delete this.renderTo;
15290     }
15291 };
15292
15293 /** @private */
15294 Roo.Component.AUTO_ID = 1000;
15295
15296 Roo.extend(Roo.Component, Roo.util.Observable, {
15297     /**
15298      * @scope Roo.Component.prototype
15299      * @type {Boolean}
15300      * true if this component is hidden. Read-only.
15301      */
15302     hidden : false,
15303     /**
15304      * @type {Boolean}
15305      * true if this component is disabled. Read-only.
15306      */
15307     disabled : false,
15308     /**
15309      * @type {Boolean}
15310      * true if this component has been rendered. Read-only.
15311      */
15312     rendered : false,
15313     
15314     /** @cfg {String} disableClass
15315      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15316      */
15317     disabledClass : "x-item-disabled",
15318         /** @cfg {Boolean} allowDomMove
15319          * Whether the component can move the Dom node when rendering (defaults to true).
15320          */
15321     allowDomMove : true,
15322     /** @cfg {String} hideMode (display|visibility)
15323      * How this component should hidden. Supported values are
15324      * "visibility" (css visibility), "offsets" (negative offset position) and
15325      * "display" (css display) - defaults to "display".
15326      */
15327     hideMode: 'display',
15328
15329     /** @private */
15330     ctype : "Roo.Component",
15331
15332     /**
15333      * @cfg {String} actionMode 
15334      * which property holds the element that used for  hide() / show() / disable() / enable()
15335      * default is 'el' 
15336      */
15337     actionMode : "el",
15338
15339     /** @private */
15340     getActionEl : function(){
15341         return this[this.actionMode];
15342     },
15343
15344     initComponent : Roo.emptyFn,
15345     /**
15346      * If this is a lazy rendering component, render it to its container element.
15347      * @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.
15348      */
15349     render : function(container, position){
15350         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15351             if(!container && this.el){
15352                 this.el = Roo.get(this.el);
15353                 container = this.el.dom.parentNode;
15354                 this.allowDomMove = false;
15355             }
15356             this.container = Roo.get(container);
15357             this.rendered = true;
15358             if(position !== undefined){
15359                 if(typeof position == 'number'){
15360                     position = this.container.dom.childNodes[position];
15361                 }else{
15362                     position = Roo.getDom(position);
15363                 }
15364             }
15365             this.onRender(this.container, position || null);
15366             if(this.cls){
15367                 this.el.addClass(this.cls);
15368                 delete this.cls;
15369             }
15370             if(this.style){
15371                 this.el.applyStyles(this.style);
15372                 delete this.style;
15373             }
15374             this.fireEvent("render", this);
15375             this.afterRender(this.container);
15376             if(this.hidden){
15377                 this.hide();
15378             }
15379             if(this.disabled){
15380                 this.disable();
15381             }
15382         }
15383         return this;
15384     },
15385
15386     /** @private */
15387     // default function is not really useful
15388     onRender : function(ct, position){
15389         if(this.el){
15390             this.el = Roo.get(this.el);
15391             if(this.allowDomMove !== false){
15392                 ct.dom.insertBefore(this.el.dom, position);
15393             }
15394         }
15395     },
15396
15397     /** @private */
15398     getAutoCreate : function(){
15399         var cfg = typeof this.autoCreate == "object" ?
15400                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15401         if(this.id && !cfg.id){
15402             cfg.id = this.id;
15403         }
15404         return cfg;
15405     },
15406
15407     /** @private */
15408     afterRender : Roo.emptyFn,
15409
15410     /**
15411      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15412      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15413      */
15414     destroy : function(){
15415         if(this.fireEvent("beforedestroy", this) !== false){
15416             this.purgeListeners();
15417             this.beforeDestroy();
15418             if(this.rendered){
15419                 this.el.removeAllListeners();
15420                 this.el.remove();
15421                 if(this.actionMode == "container"){
15422                     this.container.remove();
15423                 }
15424             }
15425             this.onDestroy();
15426             Roo.ComponentMgr.unregister(this);
15427             this.fireEvent("destroy", this);
15428         }
15429     },
15430
15431         /** @private */
15432     beforeDestroy : function(){
15433
15434     },
15435
15436         /** @private */
15437         onDestroy : function(){
15438
15439     },
15440
15441     /**
15442      * Returns the underlying {@link Roo.Element}.
15443      * @return {Roo.Element} The element
15444      */
15445     getEl : function(){
15446         return this.el;
15447     },
15448
15449     /**
15450      * Returns the id of this component.
15451      * @return {String}
15452      */
15453     getId : function(){
15454         return this.id;
15455     },
15456
15457     /**
15458      * Try to focus this component.
15459      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15460      * @return {Roo.Component} this
15461      */
15462     focus : function(selectText){
15463         if(this.rendered){
15464             this.el.focus();
15465             if(selectText === true){
15466                 this.el.dom.select();
15467             }
15468         }
15469         return this;
15470     },
15471
15472     /** @private */
15473     blur : function(){
15474         if(this.rendered){
15475             this.el.blur();
15476         }
15477         return this;
15478     },
15479
15480     /**
15481      * Disable this component.
15482      * @return {Roo.Component} this
15483      */
15484     disable : function(){
15485         if(this.rendered){
15486             this.onDisable();
15487         }
15488         this.disabled = true;
15489         this.fireEvent("disable", this);
15490         return this;
15491     },
15492
15493         // private
15494     onDisable : function(){
15495         this.getActionEl().addClass(this.disabledClass);
15496         this.el.dom.disabled = true;
15497     },
15498
15499     /**
15500      * Enable this component.
15501      * @return {Roo.Component} this
15502      */
15503     enable : function(){
15504         if(this.rendered){
15505             this.onEnable();
15506         }
15507         this.disabled = false;
15508         this.fireEvent("enable", this);
15509         return this;
15510     },
15511
15512         // private
15513     onEnable : function(){
15514         this.getActionEl().removeClass(this.disabledClass);
15515         this.el.dom.disabled = false;
15516     },
15517
15518     /**
15519      * Convenience function for setting disabled/enabled by boolean.
15520      * @param {Boolean} disabled
15521      */
15522     setDisabled : function(disabled){
15523         this[disabled ? "disable" : "enable"]();
15524     },
15525
15526     /**
15527      * Show this component.
15528      * @return {Roo.Component} this
15529      */
15530     show: function(){
15531         if(this.fireEvent("beforeshow", this) !== false){
15532             this.hidden = false;
15533             if(this.rendered){
15534                 this.onShow();
15535             }
15536             this.fireEvent("show", this);
15537         }
15538         return this;
15539     },
15540
15541     // private
15542     onShow : function(){
15543         var ae = this.getActionEl();
15544         if(this.hideMode == 'visibility'){
15545             ae.dom.style.visibility = "visible";
15546         }else if(this.hideMode == 'offsets'){
15547             ae.removeClass('x-hidden');
15548         }else{
15549             ae.dom.style.display = "";
15550         }
15551     },
15552
15553     /**
15554      * Hide this component.
15555      * @return {Roo.Component} this
15556      */
15557     hide: function(){
15558         if(this.fireEvent("beforehide", this) !== false){
15559             this.hidden = true;
15560             if(this.rendered){
15561                 this.onHide();
15562             }
15563             this.fireEvent("hide", this);
15564         }
15565         return this;
15566     },
15567
15568     // private
15569     onHide : function(){
15570         var ae = this.getActionEl();
15571         if(this.hideMode == 'visibility'){
15572             ae.dom.style.visibility = "hidden";
15573         }else if(this.hideMode == 'offsets'){
15574             ae.addClass('x-hidden');
15575         }else{
15576             ae.dom.style.display = "none";
15577         }
15578     },
15579
15580     /**
15581      * Convenience function to hide or show this component by boolean.
15582      * @param {Boolean} visible True to show, false to hide
15583      * @return {Roo.Component} this
15584      */
15585     setVisible: function(visible){
15586         if(visible) {
15587             this.show();
15588         }else{
15589             this.hide();
15590         }
15591         return this;
15592     },
15593
15594     /**
15595      * Returns true if this component is visible.
15596      */
15597     isVisible : function(){
15598         return this.getActionEl().isVisible();
15599     },
15600
15601     cloneConfig : function(overrides){
15602         overrides = overrides || {};
15603         var id = overrides.id || Roo.id();
15604         var cfg = Roo.applyIf(overrides, this.initialConfig);
15605         cfg.id = id; // prevent dup id
15606         return new this.constructor(cfg);
15607     }
15608 });/*
15609  * Based on:
15610  * Ext JS Library 1.1.1
15611  * Copyright(c) 2006-2007, Ext JS, LLC.
15612  *
15613  * Originally Released Under LGPL - original licence link has changed is not relivant.
15614  *
15615  * Fork - LGPL
15616  * <script type="text/javascript">
15617  */
15618
15619 /**
15620  * @class Roo.BoxComponent
15621  * @extends Roo.Component
15622  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15623  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15624  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15625  * layout containers.
15626  * @constructor
15627  * @param {Roo.Element/String/Object} config The configuration options.
15628  */
15629 Roo.BoxComponent = function(config){
15630     Roo.Component.call(this, config);
15631     this.addEvents({
15632         /**
15633          * @event resize
15634          * Fires after the component is resized.
15635              * @param {Roo.Component} this
15636              * @param {Number} adjWidth The box-adjusted width that was set
15637              * @param {Number} adjHeight The box-adjusted height that was set
15638              * @param {Number} rawWidth The width that was originally specified
15639              * @param {Number} rawHeight The height that was originally specified
15640              */
15641         resize : true,
15642         /**
15643          * @event move
15644          * Fires after the component is moved.
15645              * @param {Roo.Component} this
15646              * @param {Number} x The new x position
15647              * @param {Number} y The new y position
15648              */
15649         move : true
15650     });
15651 };
15652
15653 Roo.extend(Roo.BoxComponent, Roo.Component, {
15654     // private, set in afterRender to signify that the component has been rendered
15655     boxReady : false,
15656     // private, used to defer height settings to subclasses
15657     deferHeight: false,
15658     /** @cfg {Number} width
15659      * width (optional) size of component
15660      */
15661      /** @cfg {Number} height
15662      * height (optional) size of component
15663      */
15664      
15665     /**
15666      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15667      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15668      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15669      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15670      * @return {Roo.BoxComponent} this
15671      */
15672     setSize : function(w, h){
15673         // support for standard size objects
15674         if(typeof w == 'object'){
15675             h = w.height;
15676             w = w.width;
15677         }
15678         // not rendered
15679         if(!this.boxReady){
15680             this.width = w;
15681             this.height = h;
15682             return this;
15683         }
15684
15685         // prevent recalcs when not needed
15686         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15687             return this;
15688         }
15689         this.lastSize = {width: w, height: h};
15690
15691         var adj = this.adjustSize(w, h);
15692         var aw = adj.width, ah = adj.height;
15693         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15694             var rz = this.getResizeEl();
15695             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15696                 rz.setSize(aw, ah);
15697             }else if(!this.deferHeight && ah !== undefined){
15698                 rz.setHeight(ah);
15699             }else if(aw !== undefined){
15700                 rz.setWidth(aw);
15701             }
15702             this.onResize(aw, ah, w, h);
15703             this.fireEvent('resize', this, aw, ah, w, h);
15704         }
15705         return this;
15706     },
15707
15708     /**
15709      * Gets the current size of the component's underlying element.
15710      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15711      */
15712     getSize : function(){
15713         return this.el.getSize();
15714     },
15715
15716     /**
15717      * Gets the current XY position of the component's underlying element.
15718      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15719      * @return {Array} The XY position of the element (e.g., [100, 200])
15720      */
15721     getPosition : function(local){
15722         if(local === true){
15723             return [this.el.getLeft(true), this.el.getTop(true)];
15724         }
15725         return this.xy || this.el.getXY();
15726     },
15727
15728     /**
15729      * Gets the current box measurements of the component's underlying element.
15730      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15731      * @returns {Object} box An object in the format {x, y, width, height}
15732      */
15733     getBox : function(local){
15734         var s = this.el.getSize();
15735         if(local){
15736             s.x = this.el.getLeft(true);
15737             s.y = this.el.getTop(true);
15738         }else{
15739             var xy = this.xy || this.el.getXY();
15740             s.x = xy[0];
15741             s.y = xy[1];
15742         }
15743         return s;
15744     },
15745
15746     /**
15747      * Sets the current box measurements of the component's underlying element.
15748      * @param {Object} box An object in the format {x, y, width, height}
15749      * @returns {Roo.BoxComponent} this
15750      */
15751     updateBox : function(box){
15752         this.setSize(box.width, box.height);
15753         this.setPagePosition(box.x, box.y);
15754         return this;
15755     },
15756
15757     // protected
15758     getResizeEl : function(){
15759         return this.resizeEl || this.el;
15760     },
15761
15762     // protected
15763     getPositionEl : function(){
15764         return this.positionEl || this.el;
15765     },
15766
15767     /**
15768      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15769      * This method fires the move event.
15770      * @param {Number} left The new left
15771      * @param {Number} top The new top
15772      * @returns {Roo.BoxComponent} this
15773      */
15774     setPosition : function(x, y){
15775         this.x = x;
15776         this.y = y;
15777         if(!this.boxReady){
15778             return this;
15779         }
15780         var adj = this.adjustPosition(x, y);
15781         var ax = adj.x, ay = adj.y;
15782
15783         var el = this.getPositionEl();
15784         if(ax !== undefined || ay !== undefined){
15785             if(ax !== undefined && ay !== undefined){
15786                 el.setLeftTop(ax, ay);
15787             }else if(ax !== undefined){
15788                 el.setLeft(ax);
15789             }else if(ay !== undefined){
15790                 el.setTop(ay);
15791             }
15792             this.onPosition(ax, ay);
15793             this.fireEvent('move', this, ax, ay);
15794         }
15795         return this;
15796     },
15797
15798     /**
15799      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15800      * This method fires the move event.
15801      * @param {Number} x The new x position
15802      * @param {Number} y The new y position
15803      * @returns {Roo.BoxComponent} this
15804      */
15805     setPagePosition : function(x, y){
15806         this.pageX = x;
15807         this.pageY = y;
15808         if(!this.boxReady){
15809             return;
15810         }
15811         if(x === undefined || y === undefined){ // cannot translate undefined points
15812             return;
15813         }
15814         var p = this.el.translatePoints(x, y);
15815         this.setPosition(p.left, p.top);
15816         return this;
15817     },
15818
15819     // private
15820     onRender : function(ct, position){
15821         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15822         if(this.resizeEl){
15823             this.resizeEl = Roo.get(this.resizeEl);
15824         }
15825         if(this.positionEl){
15826             this.positionEl = Roo.get(this.positionEl);
15827         }
15828     },
15829
15830     // private
15831     afterRender : function(){
15832         Roo.BoxComponent.superclass.afterRender.call(this);
15833         this.boxReady = true;
15834         this.setSize(this.width, this.height);
15835         if(this.x || this.y){
15836             this.setPosition(this.x, this.y);
15837         }
15838         if(this.pageX || this.pageY){
15839             this.setPagePosition(this.pageX, this.pageY);
15840         }
15841     },
15842
15843     /**
15844      * Force the component's size to recalculate based on the underlying element's current height and width.
15845      * @returns {Roo.BoxComponent} this
15846      */
15847     syncSize : function(){
15848         delete this.lastSize;
15849         this.setSize(this.el.getWidth(), this.el.getHeight());
15850         return this;
15851     },
15852
15853     /**
15854      * Called after the component is resized, this method is empty by default but can be implemented by any
15855      * subclass that needs to perform custom logic after a resize occurs.
15856      * @param {Number} adjWidth The box-adjusted width that was set
15857      * @param {Number} adjHeight The box-adjusted height that was set
15858      * @param {Number} rawWidth The width that was originally specified
15859      * @param {Number} rawHeight The height that was originally specified
15860      */
15861     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15862
15863     },
15864
15865     /**
15866      * Called after the component is moved, this method is empty by default but can be implemented by any
15867      * subclass that needs to perform custom logic after a move occurs.
15868      * @param {Number} x The new x position
15869      * @param {Number} y The new y position
15870      */
15871     onPosition : function(x, y){
15872
15873     },
15874
15875     // private
15876     adjustSize : function(w, h){
15877         if(this.autoWidth){
15878             w = 'auto';
15879         }
15880         if(this.autoHeight){
15881             h = 'auto';
15882         }
15883         return {width : w, height: h};
15884     },
15885
15886     // private
15887     adjustPosition : function(x, y){
15888         return {x : x, y: y};
15889     }
15890 });/*
15891  * Original code for Roojs - LGPL
15892  * <script type="text/javascript">
15893  */
15894  
15895 /**
15896  * @class Roo.XComponent
15897  * A delayed Element creator...
15898  * Or a way to group chunks of interface together.
15899  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15900  *  used in conjunction with XComponent.build() it will create an instance of each element,
15901  *  then call addxtype() to build the User interface.
15902  * 
15903  * Mypart.xyx = new Roo.XComponent({
15904
15905     parent : 'Mypart.xyz', // empty == document.element.!!
15906     order : '001',
15907     name : 'xxxx'
15908     region : 'xxxx'
15909     disabled : function() {} 
15910      
15911     tree : function() { // return an tree of xtype declared components
15912         var MODULE = this;
15913         return 
15914         {
15915             xtype : 'NestedLayoutPanel',
15916             // technicall
15917         }
15918      ]
15919  *})
15920  *
15921  *
15922  * It can be used to build a big heiracy, with parent etc.
15923  * or you can just use this to render a single compoent to a dom element
15924  * MYPART.render(Roo.Element | String(id) | dom_element )
15925  *
15926  *
15927  * Usage patterns.
15928  *
15929  * Classic Roo
15930  *
15931  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15932  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15933  *
15934  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15935  *
15936  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15937  * - if mulitple topModules exist, the last one is defined as the top module.
15938  *
15939  * Embeded Roo
15940  * 
15941  * When the top level or multiple modules are to embedded into a existing HTML page,
15942  * the parent element can container '#id' of the element where the module will be drawn.
15943  *
15944  * Bootstrap Roo
15945  *
15946  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15947  * it relies more on a include mechanism, where sub modules are included into an outer page.
15948  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15949  * 
15950  * Bootstrap Roo Included elements
15951  *
15952  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15953  * hence confusing the component builder as it thinks there are multiple top level elements. 
15954  *
15955  * 
15956  * 
15957  * @extends Roo.util.Observable
15958  * @constructor
15959  * @param cfg {Object} configuration of component
15960  * 
15961  */
15962 Roo.XComponent = function(cfg) {
15963     Roo.apply(this, cfg);
15964     this.addEvents({ 
15965         /**
15966              * @event built
15967              * Fires when this the componnt is built
15968              * @param {Roo.XComponent} c the component
15969              */
15970         'built' : true
15971         
15972     });
15973     this.region = this.region || 'center'; // default..
15974     Roo.XComponent.register(this);
15975     this.modules = false;
15976     this.el = false; // where the layout goes..
15977     
15978     
15979 }
15980 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15981     /**
15982      * @property el
15983      * The created element (with Roo.factory())
15984      * @type {Roo.Layout}
15985      */
15986     el  : false,
15987     
15988     /**
15989      * @property el
15990      * for BC  - use el in new code
15991      * @type {Roo.Layout}
15992      */
15993     panel : false,
15994     
15995     /**
15996      * @property layout
15997      * for BC  - use el in new code
15998      * @type {Roo.Layout}
15999      */
16000     layout : false,
16001     
16002      /**
16003      * @cfg {Function|boolean} disabled
16004      * If this module is disabled by some rule, return true from the funtion
16005      */
16006     disabled : false,
16007     
16008     /**
16009      * @cfg {String} parent 
16010      * Name of parent element which it get xtype added to..
16011      */
16012     parent: false,
16013     
16014     /**
16015      * @cfg {String} order
16016      * Used to set the order in which elements are created (usefull for multiple tabs)
16017      */
16018     
16019     order : false,
16020     /**
16021      * @cfg {String} name
16022      * String to display while loading.
16023      */
16024     name : false,
16025     /**
16026      * @cfg {String} region
16027      * Region to render component to (defaults to center)
16028      */
16029     region : 'center',
16030     
16031     /**
16032      * @cfg {Array} items
16033      * A single item array - the first element is the root of the tree..
16034      * It's done this way to stay compatible with the Xtype system...
16035      */
16036     items : false,
16037     
16038     /**
16039      * @property _tree
16040      * The method that retuns the tree of parts that make up this compoennt 
16041      * @type {function}
16042      */
16043     _tree  : false,
16044     
16045      /**
16046      * render
16047      * render element to dom or tree
16048      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16049      */
16050     
16051     render : function(el)
16052     {
16053         
16054         el = el || false;
16055         var hp = this.parent ? 1 : 0;
16056         Roo.debug &&  Roo.log(this);
16057         
16058         var tree = this._tree ? this._tree() : this.tree();
16059
16060         
16061         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16062             // if parent is a '#.....' string, then let's use that..
16063             var ename = this.parent.substr(1);
16064             this.parent = false;
16065             Roo.debug && Roo.log(ename);
16066             switch (ename) {
16067                 case 'bootstrap-body':
16068                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16069                         // this is the BorderLayout standard?
16070                        this.parent = { el : true };
16071                        break;
16072                     }
16073                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16074                         // need to insert stuff...
16075                         this.parent =  {
16076                              el : new Roo.bootstrap.layout.Border({
16077                                  el : document.body, 
16078                      
16079                                  center: {
16080                                     titlebar: false,
16081                                     autoScroll:false,
16082                                     closeOnTab: true,
16083                                     tabPosition: 'top',
16084                                       //resizeTabs: true,
16085                                     alwaysShowTabs: true,
16086                                     hideTabs: false
16087                                      //minTabWidth: 140
16088                                  }
16089                              })
16090                         
16091                          };
16092                          break;
16093                     }
16094                          
16095                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16096                         this.parent = { el :  new  Roo.bootstrap.Body() };
16097                         Roo.debug && Roo.log("setting el to doc body");
16098                          
16099                     } else {
16100                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16101                     }
16102                     break;
16103                 case 'bootstrap':
16104                     this.parent = { el : true};
16105                     // fall through
16106                 default:
16107                     el = Roo.get(ename);
16108                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16109                         this.parent = { el : true};
16110                     }
16111                     
16112                     break;
16113             }
16114                 
16115             
16116             if (!el && !this.parent) {
16117                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16118                 return;
16119             }
16120         }
16121         
16122         Roo.debug && Roo.log("EL:");
16123         Roo.debug && Roo.log(el);
16124         Roo.debug && Roo.log("this.parent.el:");
16125         Roo.debug && Roo.log(this.parent.el);
16126         
16127
16128         // altertive root elements ??? - we need a better way to indicate these.
16129         var is_alt = Roo.XComponent.is_alt ||
16130                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16131                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16132                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16133         
16134         
16135         
16136         if (!this.parent && is_alt) {
16137             //el = Roo.get(document.body);
16138             this.parent = { el : true };
16139         }
16140             
16141             
16142         
16143         if (!this.parent) {
16144             
16145             Roo.debug && Roo.log("no parent - creating one");
16146             
16147             el = el ? Roo.get(el) : false;      
16148             
16149             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16150                 
16151                 this.parent =  {
16152                     el : new Roo.bootstrap.layout.Border({
16153                         el: el || document.body,
16154                     
16155                         center: {
16156                             titlebar: false,
16157                             autoScroll:false,
16158                             closeOnTab: true,
16159                             tabPosition: 'top',
16160                              //resizeTabs: true,
16161                             alwaysShowTabs: false,
16162                             hideTabs: true,
16163                             minTabWidth: 140,
16164                             overflow: 'visible'
16165                          }
16166                      })
16167                 };
16168             } else {
16169             
16170                 // it's a top level one..
16171                 this.parent =  {
16172                     el : new Roo.BorderLayout(el || document.body, {
16173                         center: {
16174                             titlebar: false,
16175                             autoScroll:false,
16176                             closeOnTab: true,
16177                             tabPosition: 'top',
16178                              //resizeTabs: true,
16179                             alwaysShowTabs: el && hp? false :  true,
16180                             hideTabs: el || !hp ? true :  false,
16181                             minTabWidth: 140
16182                          }
16183                     })
16184                 };
16185             }
16186         }
16187         
16188         if (!this.parent.el) {
16189                 // probably an old style ctor, which has been disabled.
16190                 return;
16191
16192         }
16193                 // The 'tree' method is  '_tree now' 
16194             
16195         tree.region = tree.region || this.region;
16196         var is_body = false;
16197         if (this.parent.el === true) {
16198             // bootstrap... - body..
16199             if (el) {
16200                 tree.el = el;
16201             }
16202             this.parent.el = Roo.factory(tree);
16203             is_body = true;
16204         }
16205         
16206         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16207         this.fireEvent('built', this);
16208         
16209         this.panel = this.el;
16210         this.layout = this.panel.layout;
16211         this.parentLayout = this.parent.layout  || false;  
16212          
16213     }
16214     
16215 });
16216
16217 Roo.apply(Roo.XComponent, {
16218     /**
16219      * @property  hideProgress
16220      * true to disable the building progress bar.. usefull on single page renders.
16221      * @type Boolean
16222      */
16223     hideProgress : false,
16224     /**
16225      * @property  buildCompleted
16226      * True when the builder has completed building the interface.
16227      * @type Boolean
16228      */
16229     buildCompleted : false,
16230      
16231     /**
16232      * @property  topModule
16233      * the upper most module - uses document.element as it's constructor.
16234      * @type Object
16235      */
16236      
16237     topModule  : false,
16238       
16239     /**
16240      * @property  modules
16241      * array of modules to be created by registration system.
16242      * @type {Array} of Roo.XComponent
16243      */
16244     
16245     modules : [],
16246     /**
16247      * @property  elmodules
16248      * array of modules to be created by which use #ID 
16249      * @type {Array} of Roo.XComponent
16250      */
16251      
16252     elmodules : [],
16253
16254      /**
16255      * @property  is_alt
16256      * Is an alternative Root - normally used by bootstrap or other systems,
16257      *    where the top element in the tree can wrap 'body' 
16258      * @type {boolean}  (default false)
16259      */
16260      
16261     is_alt : false,
16262     /**
16263      * @property  build_from_html
16264      * Build elements from html - used by bootstrap HTML stuff 
16265      *    - this is cleared after build is completed
16266      * @type {boolean}    (default false)
16267      */
16268      
16269     build_from_html : false,
16270     /**
16271      * Register components to be built later.
16272      *
16273      * This solves the following issues
16274      * - Building is not done on page load, but after an authentication process has occured.
16275      * - Interface elements are registered on page load
16276      * - Parent Interface elements may not be loaded before child, so this handles that..
16277      * 
16278      *
16279      * example:
16280      * 
16281      * MyApp.register({
16282           order : '000001',
16283           module : 'Pman.Tab.projectMgr',
16284           region : 'center',
16285           parent : 'Pman.layout',
16286           disabled : false,  // or use a function..
16287         })
16288      
16289      * * @param {Object} details about module
16290      */
16291     register : function(obj) {
16292                 
16293         Roo.XComponent.event.fireEvent('register', obj);
16294         switch(typeof(obj.disabled) ) {
16295                 
16296             case 'undefined':
16297                 break;
16298             
16299             case 'function':
16300                 if ( obj.disabled() ) {
16301                         return;
16302                 }
16303                 break;
16304             
16305             default:
16306                 if (obj.disabled) {
16307                         return;
16308                 }
16309                 break;
16310         }
16311                 
16312         this.modules.push(obj);
16313          
16314     },
16315     /**
16316      * convert a string to an object..
16317      * eg. 'AAA.BBB' -> finds AAA.BBB
16318
16319      */
16320     
16321     toObject : function(str)
16322     {
16323         if (!str || typeof(str) == 'object') {
16324             return str;
16325         }
16326         if (str.substring(0,1) == '#') {
16327             return str;
16328         }
16329
16330         var ar = str.split('.');
16331         var rt, o;
16332         rt = ar.shift();
16333             /** eval:var:o */
16334         try {
16335             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16336         } catch (e) {
16337             throw "Module not found : " + str;
16338         }
16339         
16340         if (o === false) {
16341             throw "Module not found : " + str;
16342         }
16343         Roo.each(ar, function(e) {
16344             if (typeof(o[e]) == 'undefined') {
16345                 throw "Module not found : " + str;
16346             }
16347             o = o[e];
16348         });
16349         
16350         return o;
16351         
16352     },
16353     
16354     
16355     /**
16356      * move modules into their correct place in the tree..
16357      * 
16358      */
16359     preBuild : function ()
16360     {
16361         var _t = this;
16362         Roo.each(this.modules , function (obj)
16363         {
16364             Roo.XComponent.event.fireEvent('beforebuild', obj);
16365             
16366             var opar = obj.parent;
16367             try { 
16368                 obj.parent = this.toObject(opar);
16369             } catch(e) {
16370                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16371                 return;
16372             }
16373             
16374             if (!obj.parent) {
16375                 Roo.debug && Roo.log("GOT top level module");
16376                 Roo.debug && Roo.log(obj);
16377                 obj.modules = new Roo.util.MixedCollection(false, 
16378                     function(o) { return o.order + '' }
16379                 );
16380                 this.topModule = obj;
16381                 return;
16382             }
16383                         // parent is a string (usually a dom element name..)
16384             if (typeof(obj.parent) == 'string') {
16385                 this.elmodules.push(obj);
16386                 return;
16387             }
16388             if (obj.parent.constructor != Roo.XComponent) {
16389                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16390             }
16391             if (!obj.parent.modules) {
16392                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16393                     function(o) { return o.order + '' }
16394                 );
16395             }
16396             if (obj.parent.disabled) {
16397                 obj.disabled = true;
16398             }
16399             obj.parent.modules.add(obj);
16400         }, this);
16401     },
16402     
16403      /**
16404      * make a list of modules to build.
16405      * @return {Array} list of modules. 
16406      */ 
16407     
16408     buildOrder : function()
16409     {
16410         var _this = this;
16411         var cmp = function(a,b) {   
16412             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16413         };
16414         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16415             throw "No top level modules to build";
16416         }
16417         
16418         // make a flat list in order of modules to build.
16419         var mods = this.topModule ? [ this.topModule ] : [];
16420                 
16421         
16422         // elmodules (is a list of DOM based modules )
16423         Roo.each(this.elmodules, function(e) {
16424             mods.push(e);
16425             if (!this.topModule &&
16426                 typeof(e.parent) == 'string' &&
16427                 e.parent.substring(0,1) == '#' &&
16428                 Roo.get(e.parent.substr(1))
16429                ) {
16430                 
16431                 _this.topModule = e;
16432             }
16433             
16434         });
16435
16436         
16437         // add modules to their parents..
16438         var addMod = function(m) {
16439             Roo.debug && Roo.log("build Order: add: " + m.name);
16440                 
16441             mods.push(m);
16442             if (m.modules && !m.disabled) {
16443                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16444                 m.modules.keySort('ASC',  cmp );
16445                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16446     
16447                 m.modules.each(addMod);
16448             } else {
16449                 Roo.debug && Roo.log("build Order: no child modules");
16450             }
16451             // not sure if this is used any more..
16452             if (m.finalize) {
16453                 m.finalize.name = m.name + " (clean up) ";
16454                 mods.push(m.finalize);
16455             }
16456             
16457         }
16458         if (this.topModule && this.topModule.modules) { 
16459             this.topModule.modules.keySort('ASC',  cmp );
16460             this.topModule.modules.each(addMod);
16461         } 
16462         return mods;
16463     },
16464     
16465      /**
16466      * Build the registered modules.
16467      * @param {Object} parent element.
16468      * @param {Function} optional method to call after module has been added.
16469      * 
16470      */ 
16471    
16472     build : function(opts) 
16473     {
16474         
16475         if (typeof(opts) != 'undefined') {
16476             Roo.apply(this,opts);
16477         }
16478         
16479         this.preBuild();
16480         var mods = this.buildOrder();
16481       
16482         //this.allmods = mods;
16483         //Roo.debug && Roo.log(mods);
16484         //return;
16485         if (!mods.length) { // should not happen
16486             throw "NO modules!!!";
16487         }
16488         
16489         
16490         var msg = "Building Interface...";
16491         // flash it up as modal - so we store the mask!?
16492         if (!this.hideProgress && Roo.MessageBox) {
16493             Roo.MessageBox.show({ title: 'loading' });
16494             Roo.MessageBox.show({
16495                title: "Please wait...",
16496                msg: msg,
16497                width:450,
16498                progress:true,
16499                closable:false,
16500                modal: false
16501               
16502             });
16503         }
16504         var total = mods.length;
16505         
16506         var _this = this;
16507         var progressRun = function() {
16508             if (!mods.length) {
16509                 Roo.debug && Roo.log('hide?');
16510                 if (!this.hideProgress && Roo.MessageBox) {
16511                     Roo.MessageBox.hide();
16512                 }
16513                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16514                 
16515                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16516                 
16517                 // THE END...
16518                 return false;   
16519             }
16520             
16521             var m = mods.shift();
16522             
16523             
16524             Roo.debug && Roo.log(m);
16525             // not sure if this is supported any more.. - modules that are are just function
16526             if (typeof(m) == 'function') { 
16527                 m.call(this);
16528                 return progressRun.defer(10, _this);
16529             } 
16530             
16531             
16532             msg = "Building Interface " + (total  - mods.length) + 
16533                     " of " + total + 
16534                     (m.name ? (' - ' + m.name) : '');
16535                         Roo.debug && Roo.log(msg);
16536             if (!_this.hideProgress &&  Roo.MessageBox) { 
16537                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16538             }
16539             
16540          
16541             // is the module disabled?
16542             var disabled = (typeof(m.disabled) == 'function') ?
16543                 m.disabled.call(m.module.disabled) : m.disabled;    
16544             
16545             
16546             if (disabled) {
16547                 return progressRun(); // we do not update the display!
16548             }
16549             
16550             // now build 
16551             
16552                         
16553                         
16554             m.render();
16555             // it's 10 on top level, and 1 on others??? why...
16556             return progressRun.defer(10, _this);
16557              
16558         }
16559         progressRun.defer(1, _this);
16560      
16561         
16562         
16563     },
16564         
16565         
16566         /**
16567          * Event Object.
16568          *
16569          *
16570          */
16571         event: false, 
16572     /**
16573          * wrapper for event.on - aliased later..  
16574          * Typically use to register a event handler for register:
16575          *
16576          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16577          *
16578          */
16579     on : false
16580    
16581     
16582     
16583 });
16584
16585 Roo.XComponent.event = new Roo.util.Observable({
16586                 events : { 
16587                         /**
16588                          * @event register
16589                          * Fires when an Component is registered,
16590                          * set the disable property on the Component to stop registration.
16591                          * @param {Roo.XComponent} c the component being registerd.
16592                          * 
16593                          */
16594                         'register' : true,
16595             /**
16596                          * @event beforebuild
16597                          * Fires before each Component is built
16598                          * can be used to apply permissions.
16599                          * @param {Roo.XComponent} c the component being registerd.
16600                          * 
16601                          */
16602                         'beforebuild' : true,
16603                         /**
16604                          * @event buildcomplete
16605                          * Fires on the top level element when all elements have been built
16606                          * @param {Roo.XComponent} the top level component.
16607                          */
16608                         'buildcomplete' : true
16609                         
16610                 }
16611 });
16612
16613 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16614  //
16615  /**
16616  * marked - a markdown parser
16617  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16618  * https://github.com/chjj/marked
16619  */
16620
16621
16622 /**
16623  *
16624  * Roo.Markdown - is a very crude wrapper around marked..
16625  *
16626  * usage:
16627  * 
16628  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16629  * 
16630  * Note: move the sample code to the bottom of this
16631  * file before uncommenting it.
16632  *
16633  */
16634
16635 Roo.Markdown = {};
16636 Roo.Markdown.toHtml = function(text) {
16637     
16638     var c = new Roo.Markdown.marked.setOptions({
16639             renderer: new Roo.Markdown.marked.Renderer(),
16640             gfm: true,
16641             tables: true,
16642             breaks: false,
16643             pedantic: false,
16644             sanitize: false,
16645             smartLists: true,
16646             smartypants: false
16647           });
16648     // A FEW HACKS!!?
16649     
16650     text = text.replace(/\\\n/g,' ');
16651     return Roo.Markdown.marked(text);
16652 };
16653 //
16654 // converter
16655 //
16656 // Wraps all "globals" so that the only thing
16657 // exposed is makeHtml().
16658 //
16659 (function() {
16660     
16661     /**
16662      * Block-Level Grammar
16663      */
16664     
16665     var block = {
16666       newline: /^\n+/,
16667       code: /^( {4}[^\n]+\n*)+/,
16668       fences: noop,
16669       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16670       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16671       nptable: noop,
16672       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16673       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16674       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16675       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16676       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16677       table: noop,
16678       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16679       text: /^[^\n]+/
16680     };
16681     
16682     block.bullet = /(?:[*+-]|\d+\.)/;
16683     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16684     block.item = replace(block.item, 'gm')
16685       (/bull/g, block.bullet)
16686       ();
16687     
16688     block.list = replace(block.list)
16689       (/bull/g, block.bullet)
16690       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16691       ('def', '\\n+(?=' + block.def.source + ')')
16692       ();
16693     
16694     block.blockquote = replace(block.blockquote)
16695       ('def', block.def)
16696       ();
16697     
16698     block._tag = '(?!(?:'
16699       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16700       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16701       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16702     
16703     block.html = replace(block.html)
16704       ('comment', /<!--[\s\S]*?-->/)
16705       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16706       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16707       (/tag/g, block._tag)
16708       ();
16709     
16710     block.paragraph = replace(block.paragraph)
16711       ('hr', block.hr)
16712       ('heading', block.heading)
16713       ('lheading', block.lheading)
16714       ('blockquote', block.blockquote)
16715       ('tag', '<' + block._tag)
16716       ('def', block.def)
16717       ();
16718     
16719     /**
16720      * Normal Block Grammar
16721      */
16722     
16723     block.normal = merge({}, block);
16724     
16725     /**
16726      * GFM Block Grammar
16727      */
16728     
16729     block.gfm = merge({}, block.normal, {
16730       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16731       paragraph: /^/,
16732       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16733     });
16734     
16735     block.gfm.paragraph = replace(block.paragraph)
16736       ('(?!', '(?!'
16737         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16738         + block.list.source.replace('\\1', '\\3') + '|')
16739       ();
16740     
16741     /**
16742      * GFM + Tables Block Grammar
16743      */
16744     
16745     block.tables = merge({}, block.gfm, {
16746       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16747       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16748     });
16749     
16750     /**
16751      * Block Lexer
16752      */
16753     
16754     function Lexer(options) {
16755       this.tokens = [];
16756       this.tokens.links = {};
16757       this.options = options || marked.defaults;
16758       this.rules = block.normal;
16759     
16760       if (this.options.gfm) {
16761         if (this.options.tables) {
16762           this.rules = block.tables;
16763         } else {
16764           this.rules = block.gfm;
16765         }
16766       }
16767     }
16768     
16769     /**
16770      * Expose Block Rules
16771      */
16772     
16773     Lexer.rules = block;
16774     
16775     /**
16776      * Static Lex Method
16777      */
16778     
16779     Lexer.lex = function(src, options) {
16780       var lexer = new Lexer(options);
16781       return lexer.lex(src);
16782     };
16783     
16784     /**
16785      * Preprocessing
16786      */
16787     
16788     Lexer.prototype.lex = function(src) {
16789       src = src
16790         .replace(/\r\n|\r/g, '\n')
16791         .replace(/\t/g, '    ')
16792         .replace(/\u00a0/g, ' ')
16793         .replace(/\u2424/g, '\n');
16794     
16795       return this.token(src, true);
16796     };
16797     
16798     /**
16799      * Lexing
16800      */
16801     
16802     Lexer.prototype.token = function(src, top, bq) {
16803       var src = src.replace(/^ +$/gm, '')
16804         , next
16805         , loose
16806         , cap
16807         , bull
16808         , b
16809         , item
16810         , space
16811         , i
16812         , l;
16813     
16814       while (src) {
16815         // newline
16816         if (cap = this.rules.newline.exec(src)) {
16817           src = src.substring(cap[0].length);
16818           if (cap[0].length > 1) {
16819             this.tokens.push({
16820               type: 'space'
16821             });
16822           }
16823         }
16824     
16825         // code
16826         if (cap = this.rules.code.exec(src)) {
16827           src = src.substring(cap[0].length);
16828           cap = cap[0].replace(/^ {4}/gm, '');
16829           this.tokens.push({
16830             type: 'code',
16831             text: !this.options.pedantic
16832               ? cap.replace(/\n+$/, '')
16833               : cap
16834           });
16835           continue;
16836         }
16837     
16838         // fences (gfm)
16839         if (cap = this.rules.fences.exec(src)) {
16840           src = src.substring(cap[0].length);
16841           this.tokens.push({
16842             type: 'code',
16843             lang: cap[2],
16844             text: cap[3] || ''
16845           });
16846           continue;
16847         }
16848     
16849         // heading
16850         if (cap = this.rules.heading.exec(src)) {
16851           src = src.substring(cap[0].length);
16852           this.tokens.push({
16853             type: 'heading',
16854             depth: cap[1].length,
16855             text: cap[2]
16856           });
16857           continue;
16858         }
16859     
16860         // table no leading pipe (gfm)
16861         if (top && (cap = this.rules.nptable.exec(src))) {
16862           src = src.substring(cap[0].length);
16863     
16864           item = {
16865             type: 'table',
16866             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16867             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16868             cells: cap[3].replace(/\n$/, '').split('\n')
16869           };
16870     
16871           for (i = 0; i < item.align.length; i++) {
16872             if (/^ *-+: *$/.test(item.align[i])) {
16873               item.align[i] = 'right';
16874             } else if (/^ *:-+: *$/.test(item.align[i])) {
16875               item.align[i] = 'center';
16876             } else if (/^ *:-+ *$/.test(item.align[i])) {
16877               item.align[i] = 'left';
16878             } else {
16879               item.align[i] = null;
16880             }
16881           }
16882     
16883           for (i = 0; i < item.cells.length; i++) {
16884             item.cells[i] = item.cells[i].split(/ *\| */);
16885           }
16886     
16887           this.tokens.push(item);
16888     
16889           continue;
16890         }
16891     
16892         // lheading
16893         if (cap = this.rules.lheading.exec(src)) {
16894           src = src.substring(cap[0].length);
16895           this.tokens.push({
16896             type: 'heading',
16897             depth: cap[2] === '=' ? 1 : 2,
16898             text: cap[1]
16899           });
16900           continue;
16901         }
16902     
16903         // hr
16904         if (cap = this.rules.hr.exec(src)) {
16905           src = src.substring(cap[0].length);
16906           this.tokens.push({
16907             type: 'hr'
16908           });
16909           continue;
16910         }
16911     
16912         // blockquote
16913         if (cap = this.rules.blockquote.exec(src)) {
16914           src = src.substring(cap[0].length);
16915     
16916           this.tokens.push({
16917             type: 'blockquote_start'
16918           });
16919     
16920           cap = cap[0].replace(/^ *> ?/gm, '');
16921     
16922           // Pass `top` to keep the current
16923           // "toplevel" state. This is exactly
16924           // how markdown.pl works.
16925           this.token(cap, top, true);
16926     
16927           this.tokens.push({
16928             type: 'blockquote_end'
16929           });
16930     
16931           continue;
16932         }
16933     
16934         // list
16935         if (cap = this.rules.list.exec(src)) {
16936           src = src.substring(cap[0].length);
16937           bull = cap[2];
16938     
16939           this.tokens.push({
16940             type: 'list_start',
16941             ordered: bull.length > 1
16942           });
16943     
16944           // Get each top-level item.
16945           cap = cap[0].match(this.rules.item);
16946     
16947           next = false;
16948           l = cap.length;
16949           i = 0;
16950     
16951           for (; i < l; i++) {
16952             item = cap[i];
16953     
16954             // Remove the list item's bullet
16955             // so it is seen as the next token.
16956             space = item.length;
16957             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16958     
16959             // Outdent whatever the
16960             // list item contains. Hacky.
16961             if (~item.indexOf('\n ')) {
16962               space -= item.length;
16963               item = !this.options.pedantic
16964                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16965                 : item.replace(/^ {1,4}/gm, '');
16966             }
16967     
16968             // Determine whether the next list item belongs here.
16969             // Backpedal if it does not belong in this list.
16970             if (this.options.smartLists && i !== l - 1) {
16971               b = block.bullet.exec(cap[i + 1])[0];
16972               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16973                 src = cap.slice(i + 1).join('\n') + src;
16974                 i = l - 1;
16975               }
16976             }
16977     
16978             // Determine whether item is loose or not.
16979             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16980             // for discount behavior.
16981             loose = next || /\n\n(?!\s*$)/.test(item);
16982             if (i !== l - 1) {
16983               next = item.charAt(item.length - 1) === '\n';
16984               if (!loose) { loose = next; }
16985             }
16986     
16987             this.tokens.push({
16988               type: loose
16989                 ? 'loose_item_start'
16990                 : 'list_item_start'
16991             });
16992     
16993             // Recurse.
16994             this.token(item, false, bq);
16995     
16996             this.tokens.push({
16997               type: 'list_item_end'
16998             });
16999           }
17000     
17001           this.tokens.push({
17002             type: 'list_end'
17003           });
17004     
17005           continue;
17006         }
17007     
17008         // html
17009         if (cap = this.rules.html.exec(src)) {
17010           src = src.substring(cap[0].length);
17011           this.tokens.push({
17012             type: this.options.sanitize
17013               ? 'paragraph'
17014               : 'html',
17015             pre: !this.options.sanitizer
17016               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17017             text: cap[0]
17018           });
17019           continue;
17020         }
17021     
17022         // def
17023         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17024           src = src.substring(cap[0].length);
17025           this.tokens.links[cap[1].toLowerCase()] = {
17026             href: cap[2],
17027             title: cap[3]
17028           };
17029           continue;
17030         }
17031     
17032         // table (gfm)
17033         if (top && (cap = this.rules.table.exec(src))) {
17034           src = src.substring(cap[0].length);
17035     
17036           item = {
17037             type: 'table',
17038             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17039             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17040             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17041           };
17042     
17043           for (i = 0; i < item.align.length; i++) {
17044             if (/^ *-+: *$/.test(item.align[i])) {
17045               item.align[i] = 'right';
17046             } else if (/^ *:-+: *$/.test(item.align[i])) {
17047               item.align[i] = 'center';
17048             } else if (/^ *:-+ *$/.test(item.align[i])) {
17049               item.align[i] = 'left';
17050             } else {
17051               item.align[i] = null;
17052             }
17053           }
17054     
17055           for (i = 0; i < item.cells.length; i++) {
17056             item.cells[i] = item.cells[i]
17057               .replace(/^ *\| *| *\| *$/g, '')
17058               .split(/ *\| */);
17059           }
17060     
17061           this.tokens.push(item);
17062     
17063           continue;
17064         }
17065     
17066         // top-level paragraph
17067         if (top && (cap = this.rules.paragraph.exec(src))) {
17068           src = src.substring(cap[0].length);
17069           this.tokens.push({
17070             type: 'paragraph',
17071             text: cap[1].charAt(cap[1].length - 1) === '\n'
17072               ? cap[1].slice(0, -1)
17073               : cap[1]
17074           });
17075           continue;
17076         }
17077     
17078         // text
17079         if (cap = this.rules.text.exec(src)) {
17080           // Top-level should never reach here.
17081           src = src.substring(cap[0].length);
17082           this.tokens.push({
17083             type: 'text',
17084             text: cap[0]
17085           });
17086           continue;
17087         }
17088     
17089         if (src) {
17090           throw new
17091             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17092         }
17093       }
17094     
17095       return this.tokens;
17096     };
17097     
17098     /**
17099      * Inline-Level Grammar
17100      */
17101     
17102     var inline = {
17103       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17104       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17105       url: noop,
17106       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17107       link: /^!?\[(inside)\]\(href\)/,
17108       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17109       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17110       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17111       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17112       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17113       br: /^ {2,}\n(?!\s*$)/,
17114       del: noop,
17115       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17116     };
17117     
17118     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17119     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17120     
17121     inline.link = replace(inline.link)
17122       ('inside', inline._inside)
17123       ('href', inline._href)
17124       ();
17125     
17126     inline.reflink = replace(inline.reflink)
17127       ('inside', inline._inside)
17128       ();
17129     
17130     /**
17131      * Normal Inline Grammar
17132      */
17133     
17134     inline.normal = merge({}, inline);
17135     
17136     /**
17137      * Pedantic Inline Grammar
17138      */
17139     
17140     inline.pedantic = merge({}, inline.normal, {
17141       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17142       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17143     });
17144     
17145     /**
17146      * GFM Inline Grammar
17147      */
17148     
17149     inline.gfm = merge({}, inline.normal, {
17150       escape: replace(inline.escape)('])', '~|])')(),
17151       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17152       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17153       text: replace(inline.text)
17154         (']|', '~]|')
17155         ('|', '|https?://|')
17156         ()
17157     });
17158     
17159     /**
17160      * GFM + Line Breaks Inline Grammar
17161      */
17162     
17163     inline.breaks = merge({}, inline.gfm, {
17164       br: replace(inline.br)('{2,}', '*')(),
17165       text: replace(inline.gfm.text)('{2,}', '*')()
17166     });
17167     
17168     /**
17169      * Inline Lexer & Compiler
17170      */
17171     
17172     function InlineLexer(links, options) {
17173       this.options = options || marked.defaults;
17174       this.links = links;
17175       this.rules = inline.normal;
17176       this.renderer = this.options.renderer || new Renderer;
17177       this.renderer.options = this.options;
17178     
17179       if (!this.links) {
17180         throw new
17181           Error('Tokens array requires a `links` property.');
17182       }
17183     
17184       if (this.options.gfm) {
17185         if (this.options.breaks) {
17186           this.rules = inline.breaks;
17187         } else {
17188           this.rules = inline.gfm;
17189         }
17190       } else if (this.options.pedantic) {
17191         this.rules = inline.pedantic;
17192       }
17193     }
17194     
17195     /**
17196      * Expose Inline Rules
17197      */
17198     
17199     InlineLexer.rules = inline;
17200     
17201     /**
17202      * Static Lexing/Compiling Method
17203      */
17204     
17205     InlineLexer.output = function(src, links, options) {
17206       var inline = new InlineLexer(links, options);
17207       return inline.output(src);
17208     };
17209     
17210     /**
17211      * Lexing/Compiling
17212      */
17213     
17214     InlineLexer.prototype.output = function(src) {
17215       var out = ''
17216         , link
17217         , text
17218         , href
17219         , cap;
17220     
17221       while (src) {
17222         // escape
17223         if (cap = this.rules.escape.exec(src)) {
17224           src = src.substring(cap[0].length);
17225           out += cap[1];
17226           continue;
17227         }
17228     
17229         // autolink
17230         if (cap = this.rules.autolink.exec(src)) {
17231           src = src.substring(cap[0].length);
17232           if (cap[2] === '@') {
17233             text = cap[1].charAt(6) === ':'
17234               ? this.mangle(cap[1].substring(7))
17235               : this.mangle(cap[1]);
17236             href = this.mangle('mailto:') + text;
17237           } else {
17238             text = escape(cap[1]);
17239             href = text;
17240           }
17241           out += this.renderer.link(href, null, text);
17242           continue;
17243         }
17244     
17245         // url (gfm)
17246         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17247           src = src.substring(cap[0].length);
17248           text = escape(cap[1]);
17249           href = text;
17250           out += this.renderer.link(href, null, text);
17251           continue;
17252         }
17253     
17254         // tag
17255         if (cap = this.rules.tag.exec(src)) {
17256           if (!this.inLink && /^<a /i.test(cap[0])) {
17257             this.inLink = true;
17258           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17259             this.inLink = false;
17260           }
17261           src = src.substring(cap[0].length);
17262           out += this.options.sanitize
17263             ? this.options.sanitizer
17264               ? this.options.sanitizer(cap[0])
17265               : escape(cap[0])
17266             : cap[0];
17267           continue;
17268         }
17269     
17270         // link
17271         if (cap = this.rules.link.exec(src)) {
17272           src = src.substring(cap[0].length);
17273           this.inLink = true;
17274           out += this.outputLink(cap, {
17275             href: cap[2],
17276             title: cap[3]
17277           });
17278           this.inLink = false;
17279           continue;
17280         }
17281     
17282         // reflink, nolink
17283         if ((cap = this.rules.reflink.exec(src))
17284             || (cap = this.rules.nolink.exec(src))) {
17285           src = src.substring(cap[0].length);
17286           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17287           link = this.links[link.toLowerCase()];
17288           if (!link || !link.href) {
17289             out += cap[0].charAt(0);
17290             src = cap[0].substring(1) + src;
17291             continue;
17292           }
17293           this.inLink = true;
17294           out += this.outputLink(cap, link);
17295           this.inLink = false;
17296           continue;
17297         }
17298     
17299         // strong
17300         if (cap = this.rules.strong.exec(src)) {
17301           src = src.substring(cap[0].length);
17302           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17303           continue;
17304         }
17305     
17306         // em
17307         if (cap = this.rules.em.exec(src)) {
17308           src = src.substring(cap[0].length);
17309           out += this.renderer.em(this.output(cap[2] || cap[1]));
17310           continue;
17311         }
17312     
17313         // code
17314         if (cap = this.rules.code.exec(src)) {
17315           src = src.substring(cap[0].length);
17316           out += this.renderer.codespan(escape(cap[2], true));
17317           continue;
17318         }
17319     
17320         // br
17321         if (cap = this.rules.br.exec(src)) {
17322           src = src.substring(cap[0].length);
17323           out += this.renderer.br();
17324           continue;
17325         }
17326     
17327         // del (gfm)
17328         if (cap = this.rules.del.exec(src)) {
17329           src = src.substring(cap[0].length);
17330           out += this.renderer.del(this.output(cap[1]));
17331           continue;
17332         }
17333     
17334         // text
17335         if (cap = this.rules.text.exec(src)) {
17336           src = src.substring(cap[0].length);
17337           out += this.renderer.text(escape(this.smartypants(cap[0])));
17338           continue;
17339         }
17340     
17341         if (src) {
17342           throw new
17343             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17344         }
17345       }
17346     
17347       return out;
17348     };
17349     
17350     /**
17351      * Compile Link
17352      */
17353     
17354     InlineLexer.prototype.outputLink = function(cap, link) {
17355       var href = escape(link.href)
17356         , title = link.title ? escape(link.title) : null;
17357     
17358       return cap[0].charAt(0) !== '!'
17359         ? this.renderer.link(href, title, this.output(cap[1]))
17360         : this.renderer.image(href, title, escape(cap[1]));
17361     };
17362     
17363     /**
17364      * Smartypants Transformations
17365      */
17366     
17367     InlineLexer.prototype.smartypants = function(text) {
17368       if (!this.options.smartypants)  { return text; }
17369       return text
17370         // em-dashes
17371         .replace(/---/g, '\u2014')
17372         // en-dashes
17373         .replace(/--/g, '\u2013')
17374         // opening singles
17375         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17376         // closing singles & apostrophes
17377         .replace(/'/g, '\u2019')
17378         // opening doubles
17379         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17380         // closing doubles
17381         .replace(/"/g, '\u201d')
17382         // ellipses
17383         .replace(/\.{3}/g, '\u2026');
17384     };
17385     
17386     /**
17387      * Mangle Links
17388      */
17389     
17390     InlineLexer.prototype.mangle = function(text) {
17391       if (!this.options.mangle) { return text; }
17392       var out = ''
17393         , l = text.length
17394         , i = 0
17395         , ch;
17396     
17397       for (; i < l; i++) {
17398         ch = text.charCodeAt(i);
17399         if (Math.random() > 0.5) {
17400           ch = 'x' + ch.toString(16);
17401         }
17402         out += '&#' + ch + ';';
17403       }
17404     
17405       return out;
17406     };
17407     
17408     /**
17409      * Renderer
17410      */
17411     
17412     function Renderer(options) {
17413       this.options = options || {};
17414     }
17415     
17416     Renderer.prototype.code = function(code, lang, escaped) {
17417       if (this.options.highlight) {
17418         var out = this.options.highlight(code, lang);
17419         if (out != null && out !== code) {
17420           escaped = true;
17421           code = out;
17422         }
17423       } else {
17424             // hack!!! - it's already escapeD?
17425             escaped = true;
17426       }
17427     
17428       if (!lang) {
17429         return '<pre><code>'
17430           + (escaped ? code : escape(code, true))
17431           + '\n</code></pre>';
17432       }
17433     
17434       return '<pre><code class="'
17435         + this.options.langPrefix
17436         + escape(lang, true)
17437         + '">'
17438         + (escaped ? code : escape(code, true))
17439         + '\n</code></pre>\n';
17440     };
17441     
17442     Renderer.prototype.blockquote = function(quote) {
17443       return '<blockquote>\n' + quote + '</blockquote>\n';
17444     };
17445     
17446     Renderer.prototype.html = function(html) {
17447       return html;
17448     };
17449     
17450     Renderer.prototype.heading = function(text, level, raw) {
17451       return '<h'
17452         + level
17453         + ' id="'
17454         + this.options.headerPrefix
17455         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17456         + '">'
17457         + text
17458         + '</h'
17459         + level
17460         + '>\n';
17461     };
17462     
17463     Renderer.prototype.hr = function() {
17464       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17465     };
17466     
17467     Renderer.prototype.list = function(body, ordered) {
17468       var type = ordered ? 'ol' : 'ul';
17469       return '<' + type + '>\n' + body + '</' + type + '>\n';
17470     };
17471     
17472     Renderer.prototype.listitem = function(text) {
17473       return '<li>' + text + '</li>\n';
17474     };
17475     
17476     Renderer.prototype.paragraph = function(text) {
17477       return '<p>' + text + '</p>\n';
17478     };
17479     
17480     Renderer.prototype.table = function(header, body) {
17481       return '<table class="table table-striped">\n'
17482         + '<thead>\n'
17483         + header
17484         + '</thead>\n'
17485         + '<tbody>\n'
17486         + body
17487         + '</tbody>\n'
17488         + '</table>\n';
17489     };
17490     
17491     Renderer.prototype.tablerow = function(content) {
17492       return '<tr>\n' + content + '</tr>\n';
17493     };
17494     
17495     Renderer.prototype.tablecell = function(content, flags) {
17496       var type = flags.header ? 'th' : 'td';
17497       var tag = flags.align
17498         ? '<' + type + ' style="text-align:' + flags.align + '">'
17499         : '<' + type + '>';
17500       return tag + content + '</' + type + '>\n';
17501     };
17502     
17503     // span level renderer
17504     Renderer.prototype.strong = function(text) {
17505       return '<strong>' + text + '</strong>';
17506     };
17507     
17508     Renderer.prototype.em = function(text) {
17509       return '<em>' + text + '</em>';
17510     };
17511     
17512     Renderer.prototype.codespan = function(text) {
17513       return '<code>' + text + '</code>';
17514     };
17515     
17516     Renderer.prototype.br = function() {
17517       return this.options.xhtml ? '<br/>' : '<br>';
17518     };
17519     
17520     Renderer.prototype.del = function(text) {
17521       return '<del>' + text + '</del>';
17522     };
17523     
17524     Renderer.prototype.link = function(href, title, text) {
17525       if (this.options.sanitize) {
17526         try {
17527           var prot = decodeURIComponent(unescape(href))
17528             .replace(/[^\w:]/g, '')
17529             .toLowerCase();
17530         } catch (e) {
17531           return '';
17532         }
17533         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17534           return '';
17535         }
17536       }
17537       var out = '<a href="' + href + '"';
17538       if (title) {
17539         out += ' title="' + title + '"';
17540       }
17541       out += '>' + text + '</a>';
17542       return out;
17543     };
17544     
17545     Renderer.prototype.image = function(href, title, text) {
17546       var out = '<img src="' + href + '" alt="' + text + '"';
17547       if (title) {
17548         out += ' title="' + title + '"';
17549       }
17550       out += this.options.xhtml ? '/>' : '>';
17551       return out;
17552     };
17553     
17554     Renderer.prototype.text = function(text) {
17555       return text;
17556     };
17557     
17558     /**
17559      * Parsing & Compiling
17560      */
17561     
17562     function Parser(options) {
17563       this.tokens = [];
17564       this.token = null;
17565       this.options = options || marked.defaults;
17566       this.options.renderer = this.options.renderer || new Renderer;
17567       this.renderer = this.options.renderer;
17568       this.renderer.options = this.options;
17569     }
17570     
17571     /**
17572      * Static Parse Method
17573      */
17574     
17575     Parser.parse = function(src, options, renderer) {
17576       var parser = new Parser(options, renderer);
17577       return parser.parse(src);
17578     };
17579     
17580     /**
17581      * Parse Loop
17582      */
17583     
17584     Parser.prototype.parse = function(src) {
17585       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17586       this.tokens = src.reverse();
17587     
17588       var out = '';
17589       while (this.next()) {
17590         out += this.tok();
17591       }
17592     
17593       return out;
17594     };
17595     
17596     /**
17597      * Next Token
17598      */
17599     
17600     Parser.prototype.next = function() {
17601       return this.token = this.tokens.pop();
17602     };
17603     
17604     /**
17605      * Preview Next Token
17606      */
17607     
17608     Parser.prototype.peek = function() {
17609       return this.tokens[this.tokens.length - 1] || 0;
17610     };
17611     
17612     /**
17613      * Parse Text Tokens
17614      */
17615     
17616     Parser.prototype.parseText = function() {
17617       var body = this.token.text;
17618     
17619       while (this.peek().type === 'text') {
17620         body += '\n' + this.next().text;
17621       }
17622     
17623       return this.inline.output(body);
17624     };
17625     
17626     /**
17627      * Parse Current Token
17628      */
17629     
17630     Parser.prototype.tok = function() {
17631       switch (this.token.type) {
17632         case 'space': {
17633           return '';
17634         }
17635         case 'hr': {
17636           return this.renderer.hr();
17637         }
17638         case 'heading': {
17639           return this.renderer.heading(
17640             this.inline.output(this.token.text),
17641             this.token.depth,
17642             this.token.text);
17643         }
17644         case 'code': {
17645           return this.renderer.code(this.token.text,
17646             this.token.lang,
17647             this.token.escaped);
17648         }
17649         case 'table': {
17650           var header = ''
17651             , body = ''
17652             , i
17653             , row
17654             , cell
17655             , flags
17656             , j;
17657     
17658           // header
17659           cell = '';
17660           for (i = 0; i < this.token.header.length; i++) {
17661             flags = { header: true, align: this.token.align[i] };
17662             cell += this.renderer.tablecell(
17663               this.inline.output(this.token.header[i]),
17664               { header: true, align: this.token.align[i] }
17665             );
17666           }
17667           header += this.renderer.tablerow(cell);
17668     
17669           for (i = 0; i < this.token.cells.length; i++) {
17670             row = this.token.cells[i];
17671     
17672             cell = '';
17673             for (j = 0; j < row.length; j++) {
17674               cell += this.renderer.tablecell(
17675                 this.inline.output(row[j]),
17676                 { header: false, align: this.token.align[j] }
17677               );
17678             }
17679     
17680             body += this.renderer.tablerow(cell);
17681           }
17682           return this.renderer.table(header, body);
17683         }
17684         case 'blockquote_start': {
17685           var body = '';
17686     
17687           while (this.next().type !== 'blockquote_end') {
17688             body += this.tok();
17689           }
17690     
17691           return this.renderer.blockquote(body);
17692         }
17693         case 'list_start': {
17694           var body = ''
17695             , ordered = this.token.ordered;
17696     
17697           while (this.next().type !== 'list_end') {
17698             body += this.tok();
17699           }
17700     
17701           return this.renderer.list(body, ordered);
17702         }
17703         case 'list_item_start': {
17704           var body = '';
17705     
17706           while (this.next().type !== 'list_item_end') {
17707             body += this.token.type === 'text'
17708               ? this.parseText()
17709               : this.tok();
17710           }
17711     
17712           return this.renderer.listitem(body);
17713         }
17714         case 'loose_item_start': {
17715           var body = '';
17716     
17717           while (this.next().type !== 'list_item_end') {
17718             body += this.tok();
17719           }
17720     
17721           return this.renderer.listitem(body);
17722         }
17723         case 'html': {
17724           var html = !this.token.pre && !this.options.pedantic
17725             ? this.inline.output(this.token.text)
17726             : this.token.text;
17727           return this.renderer.html(html);
17728         }
17729         case 'paragraph': {
17730           return this.renderer.paragraph(this.inline.output(this.token.text));
17731         }
17732         case 'text': {
17733           return this.renderer.paragraph(this.parseText());
17734         }
17735       }
17736     };
17737     
17738     /**
17739      * Helpers
17740      */
17741     
17742     function escape(html, encode) {
17743       return html
17744         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17745         .replace(/</g, '&lt;')
17746         .replace(/>/g, '&gt;')
17747         .replace(/"/g, '&quot;')
17748         .replace(/'/g, '&#39;');
17749     }
17750     
17751     function unescape(html) {
17752         // explicitly match decimal, hex, and named HTML entities 
17753       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17754         n = n.toLowerCase();
17755         if (n === 'colon') { return ':'; }
17756         if (n.charAt(0) === '#') {
17757           return n.charAt(1) === 'x'
17758             ? String.fromCharCode(parseInt(n.substring(2), 16))
17759             : String.fromCharCode(+n.substring(1));
17760         }
17761         return '';
17762       });
17763     }
17764     
17765     function replace(regex, opt) {
17766       regex = regex.source;
17767       opt = opt || '';
17768       return function self(name, val) {
17769         if (!name) { return new RegExp(regex, opt); }
17770         val = val.source || val;
17771         val = val.replace(/(^|[^\[])\^/g, '$1');
17772         regex = regex.replace(name, val);
17773         return self;
17774       };
17775     }
17776     
17777     function noop() {}
17778     noop.exec = noop;
17779     
17780     function merge(obj) {
17781       var i = 1
17782         , target
17783         , key;
17784     
17785       for (; i < arguments.length; i++) {
17786         target = arguments[i];
17787         for (key in target) {
17788           if (Object.prototype.hasOwnProperty.call(target, key)) {
17789             obj[key] = target[key];
17790           }
17791         }
17792       }
17793     
17794       return obj;
17795     }
17796     
17797     
17798     /**
17799      * Marked
17800      */
17801     
17802     function marked(src, opt, callback) {
17803       if (callback || typeof opt === 'function') {
17804         if (!callback) {
17805           callback = opt;
17806           opt = null;
17807         }
17808     
17809         opt = merge({}, marked.defaults, opt || {});
17810     
17811         var highlight = opt.highlight
17812           , tokens
17813           , pending
17814           , i = 0;
17815     
17816         try {
17817           tokens = Lexer.lex(src, opt)
17818         } catch (e) {
17819           return callback(e);
17820         }
17821     
17822         pending = tokens.length;
17823     
17824         var done = function(err) {
17825           if (err) {
17826             opt.highlight = highlight;
17827             return callback(err);
17828           }
17829     
17830           var out;
17831     
17832           try {
17833             out = Parser.parse(tokens, opt);
17834           } catch (e) {
17835             err = e;
17836           }
17837     
17838           opt.highlight = highlight;
17839     
17840           return err
17841             ? callback(err)
17842             : callback(null, out);
17843         };
17844     
17845         if (!highlight || highlight.length < 3) {
17846           return done();
17847         }
17848     
17849         delete opt.highlight;
17850     
17851         if (!pending) { return done(); }
17852     
17853         for (; i < tokens.length; i++) {
17854           (function(token) {
17855             if (token.type !== 'code') {
17856               return --pending || done();
17857             }
17858             return highlight(token.text, token.lang, function(err, code) {
17859               if (err) { return done(err); }
17860               if (code == null || code === token.text) {
17861                 return --pending || done();
17862               }
17863               token.text = code;
17864               token.escaped = true;
17865               --pending || done();
17866             });
17867           })(tokens[i]);
17868         }
17869     
17870         return;
17871       }
17872       try {
17873         if (opt) { opt = merge({}, marked.defaults, opt); }
17874         return Parser.parse(Lexer.lex(src, opt), opt);
17875       } catch (e) {
17876         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17877         if ((opt || marked.defaults).silent) {
17878           return '<p>An error occured:</p><pre>'
17879             + escape(e.message + '', true)
17880             + '</pre>';
17881         }
17882         throw e;
17883       }
17884     }
17885     
17886     /**
17887      * Options
17888      */
17889     
17890     marked.options =
17891     marked.setOptions = function(opt) {
17892       merge(marked.defaults, opt);
17893       return marked;
17894     };
17895     
17896     marked.defaults = {
17897       gfm: true,
17898       tables: true,
17899       breaks: false,
17900       pedantic: false,
17901       sanitize: false,
17902       sanitizer: null,
17903       mangle: true,
17904       smartLists: false,
17905       silent: false,
17906       highlight: null,
17907       langPrefix: 'lang-',
17908       smartypants: false,
17909       headerPrefix: '',
17910       renderer: new Renderer,
17911       xhtml: false
17912     };
17913     
17914     /**
17915      * Expose
17916      */
17917     
17918     marked.Parser = Parser;
17919     marked.parser = Parser.parse;
17920     
17921     marked.Renderer = Renderer;
17922     
17923     marked.Lexer = Lexer;
17924     marked.lexer = Lexer.lex;
17925     
17926     marked.InlineLexer = InlineLexer;
17927     marked.inlineLexer = InlineLexer.output;
17928     
17929     marked.parse = marked;
17930     
17931     Roo.Markdown.marked = marked;
17932
17933 })();/*
17934  * Based on:
17935  * Ext JS Library 1.1.1
17936  * Copyright(c) 2006-2007, Ext JS, LLC.
17937  *
17938  * Originally Released Under LGPL - original licence link has changed is not relivant.
17939  *
17940  * Fork - LGPL
17941  * <script type="text/javascript">
17942  */
17943
17944
17945
17946 /*
17947  * These classes are derivatives of the similarly named classes in the YUI Library.
17948  * The original license:
17949  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17950  * Code licensed under the BSD License:
17951  * http://developer.yahoo.net/yui/license.txt
17952  */
17953
17954 (function() {
17955
17956 var Event=Roo.EventManager;
17957 var Dom=Roo.lib.Dom;
17958
17959 /**
17960  * @class Roo.dd.DragDrop
17961  * @extends Roo.util.Observable
17962  * Defines the interface and base operation of items that that can be
17963  * dragged or can be drop targets.  It was designed to be extended, overriding
17964  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17965  * Up to three html elements can be associated with a DragDrop instance:
17966  * <ul>
17967  * <li>linked element: the element that is passed into the constructor.
17968  * This is the element which defines the boundaries for interaction with
17969  * other DragDrop objects.</li>
17970  * <li>handle element(s): The drag operation only occurs if the element that
17971  * was clicked matches a handle element.  By default this is the linked
17972  * element, but there are times that you will want only a portion of the
17973  * linked element to initiate the drag operation, and the setHandleElId()
17974  * method provides a way to define this.</li>
17975  * <li>drag element: this represents the element that would be moved along
17976  * with the cursor during a drag operation.  By default, this is the linked
17977  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17978  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17979  * </li>
17980  * </ul>
17981  * This class should not be instantiated until the onload event to ensure that
17982  * the associated elements are available.
17983  * The following would define a DragDrop obj that would interact with any
17984  * other DragDrop obj in the "group1" group:
17985  * <pre>
17986  *  dd = new Roo.dd.DragDrop("div1", "group1");
17987  * </pre>
17988  * Since none of the event handlers have been implemented, nothing would
17989  * actually happen if you were to run the code above.  Normally you would
17990  * override this class or one of the default implementations, but you can
17991  * also override the methods you want on an instance of the class...
17992  * <pre>
17993  *  dd.onDragDrop = function(e, id) {
17994  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17995  *  }
17996  * </pre>
17997  * @constructor
17998  * @param {String} id of the element that is linked to this instance
17999  * @param {String} sGroup the group of related DragDrop objects
18000  * @param {object} config an object containing configurable attributes
18001  *                Valid properties for DragDrop:
18002  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18003  */
18004 Roo.dd.DragDrop = function(id, sGroup, config) {
18005     if (id) {
18006         this.init(id, sGroup, config);
18007     }
18008     
18009 };
18010
18011 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18012
18013     /**
18014      * The id of the element associated with this object.  This is what we
18015      * refer to as the "linked element" because the size and position of
18016      * this element is used to determine when the drag and drop objects have
18017      * interacted.
18018      * @property id
18019      * @type String
18020      */
18021     id: null,
18022
18023     /**
18024      * Configuration attributes passed into the constructor
18025      * @property config
18026      * @type object
18027      */
18028     config: null,
18029
18030     /**
18031      * The id of the element that will be dragged.  By default this is same
18032      * as the linked element , but could be changed to another element. Ex:
18033      * Roo.dd.DDProxy
18034      * @property dragElId
18035      * @type String
18036      * @private
18037      */
18038     dragElId: null,
18039
18040     /**
18041      * the id of the element that initiates the drag operation.  By default
18042      * this is the linked element, but could be changed to be a child of this
18043      * element.  This lets us do things like only starting the drag when the
18044      * header element within the linked html element is clicked.
18045      * @property handleElId
18046      * @type String
18047      * @private
18048      */
18049     handleElId: null,
18050
18051     /**
18052      * An associative array of HTML tags that will be ignored if clicked.
18053      * @property invalidHandleTypes
18054      * @type {string: string}
18055      */
18056     invalidHandleTypes: null,
18057
18058     /**
18059      * An associative array of ids for elements that will be ignored if clicked
18060      * @property invalidHandleIds
18061      * @type {string: string}
18062      */
18063     invalidHandleIds: null,
18064
18065     /**
18066      * An indexted array of css class names for elements that will be ignored
18067      * if clicked.
18068      * @property invalidHandleClasses
18069      * @type string[]
18070      */
18071     invalidHandleClasses: null,
18072
18073     /**
18074      * The linked element's absolute X position at the time the drag was
18075      * started
18076      * @property startPageX
18077      * @type int
18078      * @private
18079      */
18080     startPageX: 0,
18081
18082     /**
18083      * The linked element's absolute X position at the time the drag was
18084      * started
18085      * @property startPageY
18086      * @type int
18087      * @private
18088      */
18089     startPageY: 0,
18090
18091     /**
18092      * The group defines a logical collection of DragDrop objects that are
18093      * related.  Instances only get events when interacting with other
18094      * DragDrop object in the same group.  This lets us define multiple
18095      * groups using a single DragDrop subclass if we want.
18096      * @property groups
18097      * @type {string: string}
18098      */
18099     groups: null,
18100
18101     /**
18102      * Individual drag/drop instances can be locked.  This will prevent
18103      * onmousedown start drag.
18104      * @property locked
18105      * @type boolean
18106      * @private
18107      */
18108     locked: false,
18109
18110     /**
18111      * Lock this instance
18112      * @method lock
18113      */
18114     lock: function() { this.locked = true; },
18115
18116     /**
18117      * Unlock this instace
18118      * @method unlock
18119      */
18120     unlock: function() { this.locked = false; },
18121
18122     /**
18123      * By default, all insances can be a drop target.  This can be disabled by
18124      * setting isTarget to false.
18125      * @method isTarget
18126      * @type boolean
18127      */
18128     isTarget: true,
18129
18130     /**
18131      * The padding configured for this drag and drop object for calculating
18132      * the drop zone intersection with this object.
18133      * @method padding
18134      * @type int[]
18135      */
18136     padding: null,
18137
18138     /**
18139      * Cached reference to the linked element
18140      * @property _domRef
18141      * @private
18142      */
18143     _domRef: null,
18144
18145     /**
18146      * Internal typeof flag
18147      * @property __ygDragDrop
18148      * @private
18149      */
18150     __ygDragDrop: true,
18151
18152     /**
18153      * Set to true when horizontal contraints are applied
18154      * @property constrainX
18155      * @type boolean
18156      * @private
18157      */
18158     constrainX: false,
18159
18160     /**
18161      * Set to true when vertical contraints are applied
18162      * @property constrainY
18163      * @type boolean
18164      * @private
18165      */
18166     constrainY: false,
18167
18168     /**
18169      * The left constraint
18170      * @property minX
18171      * @type int
18172      * @private
18173      */
18174     minX: 0,
18175
18176     /**
18177      * The right constraint
18178      * @property maxX
18179      * @type int
18180      * @private
18181      */
18182     maxX: 0,
18183
18184     /**
18185      * The up constraint
18186      * @property minY
18187      * @type int
18188      * @type int
18189      * @private
18190      */
18191     minY: 0,
18192
18193     /**
18194      * The down constraint
18195      * @property maxY
18196      * @type int
18197      * @private
18198      */
18199     maxY: 0,
18200
18201     /**
18202      * Maintain offsets when we resetconstraints.  Set to true when you want
18203      * the position of the element relative to its parent to stay the same
18204      * when the page changes
18205      *
18206      * @property maintainOffset
18207      * @type boolean
18208      */
18209     maintainOffset: false,
18210
18211     /**
18212      * Array of pixel locations the element will snap to if we specified a
18213      * horizontal graduation/interval.  This array is generated automatically
18214      * when you define a tick interval.
18215      * @property xTicks
18216      * @type int[]
18217      */
18218     xTicks: null,
18219
18220     /**
18221      * Array of pixel locations the element will snap to if we specified a
18222      * vertical graduation/interval.  This array is generated automatically
18223      * when you define a tick interval.
18224      * @property yTicks
18225      * @type int[]
18226      */
18227     yTicks: null,
18228
18229     /**
18230      * By default the drag and drop instance will only respond to the primary
18231      * button click (left button for a right-handed mouse).  Set to true to
18232      * allow drag and drop to start with any mouse click that is propogated
18233      * by the browser
18234      * @property primaryButtonOnly
18235      * @type boolean
18236      */
18237     primaryButtonOnly: true,
18238
18239     /**
18240      * The availabe property is false until the linked dom element is accessible.
18241      * @property available
18242      * @type boolean
18243      */
18244     available: false,
18245
18246     /**
18247      * By default, drags can only be initiated if the mousedown occurs in the
18248      * region the linked element is.  This is done in part to work around a
18249      * bug in some browsers that mis-report the mousedown if the previous
18250      * mouseup happened outside of the window.  This property is set to true
18251      * if outer handles are defined.
18252      *
18253      * @property hasOuterHandles
18254      * @type boolean
18255      * @default false
18256      */
18257     hasOuterHandles: false,
18258
18259     /**
18260      * Code that executes immediately before the startDrag event
18261      * @method b4StartDrag
18262      * @private
18263      */
18264     b4StartDrag: function(x, y) { },
18265
18266     /**
18267      * Abstract method called after a drag/drop object is clicked
18268      * and the drag or mousedown time thresholds have beeen met.
18269      * @method startDrag
18270      * @param {int} X click location
18271      * @param {int} Y click location
18272      */
18273     startDrag: function(x, y) { /* override this */ },
18274
18275     /**
18276      * Code that executes immediately before the onDrag event
18277      * @method b4Drag
18278      * @private
18279      */
18280     b4Drag: function(e) { },
18281
18282     /**
18283      * Abstract method called during the onMouseMove event while dragging an
18284      * object.
18285      * @method onDrag
18286      * @param {Event} e the mousemove event
18287      */
18288     onDrag: function(e) { /* override this */ },
18289
18290     /**
18291      * Abstract method called when this element fist begins hovering over
18292      * another DragDrop obj
18293      * @method onDragEnter
18294      * @param {Event} e the mousemove event
18295      * @param {String|DragDrop[]} id In POINT mode, the element
18296      * id this is hovering over.  In INTERSECT mode, an array of one or more
18297      * dragdrop items being hovered over.
18298      */
18299     onDragEnter: function(e, id) { /* override this */ },
18300
18301     /**
18302      * Code that executes immediately before the onDragOver event
18303      * @method b4DragOver
18304      * @private
18305      */
18306     b4DragOver: function(e) { },
18307
18308     /**
18309      * Abstract method called when this element is hovering over another
18310      * DragDrop obj
18311      * @method onDragOver
18312      * @param {Event} e the mousemove event
18313      * @param {String|DragDrop[]} id In POINT mode, the element
18314      * id this is hovering over.  In INTERSECT mode, an array of dd items
18315      * being hovered over.
18316      */
18317     onDragOver: function(e, id) { /* override this */ },
18318
18319     /**
18320      * Code that executes immediately before the onDragOut event
18321      * @method b4DragOut
18322      * @private
18323      */
18324     b4DragOut: function(e) { },
18325
18326     /**
18327      * Abstract method called when we are no longer hovering over an element
18328      * @method onDragOut
18329      * @param {Event} e the mousemove event
18330      * @param {String|DragDrop[]} id In POINT mode, the element
18331      * id this was hovering over.  In INTERSECT mode, an array of dd items
18332      * that the mouse is no longer over.
18333      */
18334     onDragOut: function(e, id) { /* override this */ },
18335
18336     /**
18337      * Code that executes immediately before the onDragDrop event
18338      * @method b4DragDrop
18339      * @private
18340      */
18341     b4DragDrop: function(e) { },
18342
18343     /**
18344      * Abstract method called when this item is dropped on another DragDrop
18345      * obj
18346      * @method onDragDrop
18347      * @param {Event} e the mouseup event
18348      * @param {String|DragDrop[]} id In POINT mode, the element
18349      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18350      * was dropped on.
18351      */
18352     onDragDrop: function(e, id) { /* override this */ },
18353
18354     /**
18355      * Abstract method called when this item is dropped on an area with no
18356      * drop target
18357      * @method onInvalidDrop
18358      * @param {Event} e the mouseup event
18359      */
18360     onInvalidDrop: function(e) { /* override this */ },
18361
18362     /**
18363      * Code that executes immediately before the endDrag event
18364      * @method b4EndDrag
18365      * @private
18366      */
18367     b4EndDrag: function(e) { },
18368
18369     /**
18370      * Fired when we are done dragging the object
18371      * @method endDrag
18372      * @param {Event} e the mouseup event
18373      */
18374     endDrag: function(e) { /* override this */ },
18375
18376     /**
18377      * Code executed immediately before the onMouseDown event
18378      * @method b4MouseDown
18379      * @param {Event} e the mousedown event
18380      * @private
18381      */
18382     b4MouseDown: function(e) {  },
18383
18384     /**
18385      * Event handler that fires when a drag/drop obj gets a mousedown
18386      * @method onMouseDown
18387      * @param {Event} e the mousedown event
18388      */
18389     onMouseDown: function(e) { /* override this */ },
18390
18391     /**
18392      * Event handler that fires when a drag/drop obj gets a mouseup
18393      * @method onMouseUp
18394      * @param {Event} e the mouseup event
18395      */
18396     onMouseUp: function(e) { /* override this */ },
18397
18398     /**
18399      * Override the onAvailable method to do what is needed after the initial
18400      * position was determined.
18401      * @method onAvailable
18402      */
18403     onAvailable: function () {
18404     },
18405
18406     /*
18407      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18408      * @type Object
18409      */
18410     defaultPadding : {left:0, right:0, top:0, bottom:0},
18411
18412     /*
18413      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18414  *
18415  * Usage:
18416  <pre><code>
18417  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18418                 { dragElId: "existingProxyDiv" });
18419  dd.startDrag = function(){
18420      this.constrainTo("parent-id");
18421  };
18422  </code></pre>
18423  * Or you can initalize it using the {@link Roo.Element} object:
18424  <pre><code>
18425  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18426      startDrag : function(){
18427          this.constrainTo("parent-id");
18428      }
18429  });
18430  </code></pre>
18431      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18432      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18433      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18434      * an object containing the sides to pad. For example: {right:10, bottom:10}
18435      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18436      */
18437     constrainTo : function(constrainTo, pad, inContent){
18438         if(typeof pad == "number"){
18439             pad = {left: pad, right:pad, top:pad, bottom:pad};
18440         }
18441         pad = pad || this.defaultPadding;
18442         var b = Roo.get(this.getEl()).getBox();
18443         var ce = Roo.get(constrainTo);
18444         var s = ce.getScroll();
18445         var c, cd = ce.dom;
18446         if(cd == document.body){
18447             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18448         }else{
18449             xy = ce.getXY();
18450             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18451         }
18452
18453
18454         var topSpace = b.y - c.y;
18455         var leftSpace = b.x - c.x;
18456
18457         this.resetConstraints();
18458         this.setXConstraint(leftSpace - (pad.left||0), // left
18459                 c.width - leftSpace - b.width - (pad.right||0) //right
18460         );
18461         this.setYConstraint(topSpace - (pad.top||0), //top
18462                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18463         );
18464     },
18465
18466     /**
18467      * Returns a reference to the linked element
18468      * @method getEl
18469      * @return {HTMLElement} the html element
18470      */
18471     getEl: function() {
18472         if (!this._domRef) {
18473             this._domRef = Roo.getDom(this.id);
18474         }
18475
18476         return this._domRef;
18477     },
18478
18479     /**
18480      * Returns a reference to the actual element to drag.  By default this is
18481      * the same as the html element, but it can be assigned to another
18482      * element. An example of this can be found in Roo.dd.DDProxy
18483      * @method getDragEl
18484      * @return {HTMLElement} the html element
18485      */
18486     getDragEl: function() {
18487         return Roo.getDom(this.dragElId);
18488     },
18489
18490     /**
18491      * Sets up the DragDrop object.  Must be called in the constructor of any
18492      * Roo.dd.DragDrop subclass
18493      * @method init
18494      * @param id the id of the linked element
18495      * @param {String} sGroup the group of related items
18496      * @param {object} config configuration attributes
18497      */
18498     init: function(id, sGroup, config) {
18499         this.initTarget(id, sGroup, config);
18500         if (!Roo.isTouch) {
18501             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18502         }
18503         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18504         // Event.on(this.id, "selectstart", Event.preventDefault);
18505     },
18506
18507     /**
18508      * Initializes Targeting functionality only... the object does not
18509      * get a mousedown handler.
18510      * @method initTarget
18511      * @param id the id of the linked element
18512      * @param {String} sGroup the group of related items
18513      * @param {object} config configuration attributes
18514      */
18515     initTarget: function(id, sGroup, config) {
18516
18517         // configuration attributes
18518         this.config = config || {};
18519
18520         // create a local reference to the drag and drop manager
18521         this.DDM = Roo.dd.DDM;
18522         // initialize the groups array
18523         this.groups = {};
18524
18525         // assume that we have an element reference instead of an id if the
18526         // parameter is not a string
18527         if (typeof id !== "string") {
18528             id = Roo.id(id);
18529         }
18530
18531         // set the id
18532         this.id = id;
18533
18534         // add to an interaction group
18535         this.addToGroup((sGroup) ? sGroup : "default");
18536
18537         // We don't want to register this as the handle with the manager
18538         // so we just set the id rather than calling the setter.
18539         this.handleElId = id;
18540
18541         // the linked element is the element that gets dragged by default
18542         this.setDragElId(id);
18543
18544         // by default, clicked anchors will not start drag operations.
18545         this.invalidHandleTypes = { A: "A" };
18546         this.invalidHandleIds = {};
18547         this.invalidHandleClasses = [];
18548
18549         this.applyConfig();
18550
18551         this.handleOnAvailable();
18552     },
18553
18554     /**
18555      * Applies the configuration parameters that were passed into the constructor.
18556      * This is supposed to happen at each level through the inheritance chain.  So
18557      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18558      * DragDrop in order to get all of the parameters that are available in
18559      * each object.
18560      * @method applyConfig
18561      */
18562     applyConfig: function() {
18563
18564         // configurable properties:
18565         //    padding, isTarget, maintainOffset, primaryButtonOnly
18566         this.padding           = this.config.padding || [0, 0, 0, 0];
18567         this.isTarget          = (this.config.isTarget !== false);
18568         this.maintainOffset    = (this.config.maintainOffset);
18569         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18570
18571     },
18572
18573     /**
18574      * Executed when the linked element is available
18575      * @method handleOnAvailable
18576      * @private
18577      */
18578     handleOnAvailable: function() {
18579         this.available = true;
18580         this.resetConstraints();
18581         this.onAvailable();
18582     },
18583
18584      /**
18585      * Configures the padding for the target zone in px.  Effectively expands
18586      * (or reduces) the virtual object size for targeting calculations.
18587      * Supports css-style shorthand; if only one parameter is passed, all sides
18588      * will have that padding, and if only two are passed, the top and bottom
18589      * will have the first param, the left and right the second.
18590      * @method setPadding
18591      * @param {int} iTop    Top pad
18592      * @param {int} iRight  Right pad
18593      * @param {int} iBot    Bot pad
18594      * @param {int} iLeft   Left pad
18595      */
18596     setPadding: function(iTop, iRight, iBot, iLeft) {
18597         // this.padding = [iLeft, iRight, iTop, iBot];
18598         if (!iRight && 0 !== iRight) {
18599             this.padding = [iTop, iTop, iTop, iTop];
18600         } else if (!iBot && 0 !== iBot) {
18601             this.padding = [iTop, iRight, iTop, iRight];
18602         } else {
18603             this.padding = [iTop, iRight, iBot, iLeft];
18604         }
18605     },
18606
18607     /**
18608      * Stores the initial placement of the linked element.
18609      * @method setInitialPosition
18610      * @param {int} diffX   the X offset, default 0
18611      * @param {int} diffY   the Y offset, default 0
18612      */
18613     setInitPosition: function(diffX, diffY) {
18614         var el = this.getEl();
18615
18616         if (!this.DDM.verifyEl(el)) {
18617             return;
18618         }
18619
18620         var dx = diffX || 0;
18621         var dy = diffY || 0;
18622
18623         var p = Dom.getXY( el );
18624
18625         this.initPageX = p[0] - dx;
18626         this.initPageY = p[1] - dy;
18627
18628         this.lastPageX = p[0];
18629         this.lastPageY = p[1];
18630
18631
18632         this.setStartPosition(p);
18633     },
18634
18635     /**
18636      * Sets the start position of the element.  This is set when the obj
18637      * is initialized, the reset when a drag is started.
18638      * @method setStartPosition
18639      * @param pos current position (from previous lookup)
18640      * @private
18641      */
18642     setStartPosition: function(pos) {
18643         var p = pos || Dom.getXY( this.getEl() );
18644         this.deltaSetXY = null;
18645
18646         this.startPageX = p[0];
18647         this.startPageY = p[1];
18648     },
18649
18650     /**
18651      * Add this instance to a group of related drag/drop objects.  All
18652      * instances belong to at least one group, and can belong to as many
18653      * groups as needed.
18654      * @method addToGroup
18655      * @param sGroup {string} the name of the group
18656      */
18657     addToGroup: function(sGroup) {
18658         this.groups[sGroup] = true;
18659         this.DDM.regDragDrop(this, sGroup);
18660     },
18661
18662     /**
18663      * Remove's this instance from the supplied interaction group
18664      * @method removeFromGroup
18665      * @param {string}  sGroup  The group to drop
18666      */
18667     removeFromGroup: function(sGroup) {
18668         if (this.groups[sGroup]) {
18669             delete this.groups[sGroup];
18670         }
18671
18672         this.DDM.removeDDFromGroup(this, sGroup);
18673     },
18674
18675     /**
18676      * Allows you to specify that an element other than the linked element
18677      * will be moved with the cursor during a drag
18678      * @method setDragElId
18679      * @param id {string} the id of the element that will be used to initiate the drag
18680      */
18681     setDragElId: function(id) {
18682         this.dragElId = id;
18683     },
18684
18685     /**
18686      * Allows you to specify a child of the linked element that should be
18687      * used to initiate the drag operation.  An example of this would be if
18688      * you have a content div with text and links.  Clicking anywhere in the
18689      * content area would normally start the drag operation.  Use this method
18690      * to specify that an element inside of the content div is the element
18691      * that starts the drag operation.
18692      * @method setHandleElId
18693      * @param id {string} the id of the element that will be used to
18694      * initiate the drag.
18695      */
18696     setHandleElId: function(id) {
18697         if (typeof id !== "string") {
18698             id = Roo.id(id);
18699         }
18700         this.handleElId = id;
18701         this.DDM.regHandle(this.id, id);
18702     },
18703
18704     /**
18705      * Allows you to set an element outside of the linked element as a drag
18706      * handle
18707      * @method setOuterHandleElId
18708      * @param id the id of the element that will be used to initiate the drag
18709      */
18710     setOuterHandleElId: function(id) {
18711         if (typeof id !== "string") {
18712             id = Roo.id(id);
18713         }
18714         Event.on(id, "mousedown",
18715                 this.handleMouseDown, this);
18716         this.setHandleElId(id);
18717
18718         this.hasOuterHandles = true;
18719     },
18720
18721     /**
18722      * Remove all drag and drop hooks for this element
18723      * @method unreg
18724      */
18725     unreg: function() {
18726         Event.un(this.id, "mousedown",
18727                 this.handleMouseDown);
18728         Event.un(this.id, "touchstart",
18729                 this.handleMouseDown);
18730         this._domRef = null;
18731         this.DDM._remove(this);
18732     },
18733
18734     destroy : function(){
18735         this.unreg();
18736     },
18737
18738     /**
18739      * Returns true if this instance is locked, or the drag drop mgr is locked
18740      * (meaning that all drag/drop is disabled on the page.)
18741      * @method isLocked
18742      * @return {boolean} true if this obj or all drag/drop is locked, else
18743      * false
18744      */
18745     isLocked: function() {
18746         return (this.DDM.isLocked() || this.locked);
18747     },
18748
18749     /**
18750      * Fired when this object is clicked
18751      * @method handleMouseDown
18752      * @param {Event} e
18753      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18754      * @private
18755      */
18756     handleMouseDown: function(e, oDD){
18757      
18758         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18759             //Roo.log('not touch/ button !=0');
18760             return;
18761         }
18762         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18763             return; // double touch..
18764         }
18765         
18766
18767         if (this.isLocked()) {
18768             //Roo.log('locked');
18769             return;
18770         }
18771
18772         this.DDM.refreshCache(this.groups);
18773 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18774         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18775         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18776             //Roo.log('no outer handes or not over target');
18777                 // do nothing.
18778         } else {
18779 //            Roo.log('check validator');
18780             if (this.clickValidator(e)) {
18781 //                Roo.log('validate success');
18782                 // set the initial element position
18783                 this.setStartPosition();
18784
18785
18786                 this.b4MouseDown(e);
18787                 this.onMouseDown(e);
18788
18789                 this.DDM.handleMouseDown(e, this);
18790
18791                 this.DDM.stopEvent(e);
18792             } else {
18793
18794
18795             }
18796         }
18797     },
18798
18799     clickValidator: function(e) {
18800         var target = e.getTarget();
18801         return ( this.isValidHandleChild(target) &&
18802                     (this.id == this.handleElId ||
18803                         this.DDM.handleWasClicked(target, this.id)) );
18804     },
18805
18806     /**
18807      * Allows you to specify a tag name that should not start a drag operation
18808      * when clicked.  This is designed to facilitate embedding links within a
18809      * drag handle that do something other than start the drag.
18810      * @method addInvalidHandleType
18811      * @param {string} tagName the type of element to exclude
18812      */
18813     addInvalidHandleType: function(tagName) {
18814         var type = tagName.toUpperCase();
18815         this.invalidHandleTypes[type] = type;
18816     },
18817
18818     /**
18819      * Lets you to specify an element id for a child of a drag handle
18820      * that should not initiate a drag
18821      * @method addInvalidHandleId
18822      * @param {string} id the element id of the element you wish to ignore
18823      */
18824     addInvalidHandleId: function(id) {
18825         if (typeof id !== "string") {
18826             id = Roo.id(id);
18827         }
18828         this.invalidHandleIds[id] = id;
18829     },
18830
18831     /**
18832      * Lets you specify a css class of elements that will not initiate a drag
18833      * @method addInvalidHandleClass
18834      * @param {string} cssClass the class of the elements you wish to ignore
18835      */
18836     addInvalidHandleClass: function(cssClass) {
18837         this.invalidHandleClasses.push(cssClass);
18838     },
18839
18840     /**
18841      * Unsets an excluded tag name set by addInvalidHandleType
18842      * @method removeInvalidHandleType
18843      * @param {string} tagName the type of element to unexclude
18844      */
18845     removeInvalidHandleType: function(tagName) {
18846         var type = tagName.toUpperCase();
18847         // this.invalidHandleTypes[type] = null;
18848         delete this.invalidHandleTypes[type];
18849     },
18850
18851     /**
18852      * Unsets an invalid handle id
18853      * @method removeInvalidHandleId
18854      * @param {string} id the id of the element to re-enable
18855      */
18856     removeInvalidHandleId: function(id) {
18857         if (typeof id !== "string") {
18858             id = Roo.id(id);
18859         }
18860         delete this.invalidHandleIds[id];
18861     },
18862
18863     /**
18864      * Unsets an invalid css class
18865      * @method removeInvalidHandleClass
18866      * @param {string} cssClass the class of the element(s) you wish to
18867      * re-enable
18868      */
18869     removeInvalidHandleClass: function(cssClass) {
18870         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18871             if (this.invalidHandleClasses[i] == cssClass) {
18872                 delete this.invalidHandleClasses[i];
18873             }
18874         }
18875     },
18876
18877     /**
18878      * Checks the tag exclusion list to see if this click should be ignored
18879      * @method isValidHandleChild
18880      * @param {HTMLElement} node the HTMLElement to evaluate
18881      * @return {boolean} true if this is a valid tag type, false if not
18882      */
18883     isValidHandleChild: function(node) {
18884
18885         var valid = true;
18886         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18887         var nodeName;
18888         try {
18889             nodeName = node.nodeName.toUpperCase();
18890         } catch(e) {
18891             nodeName = node.nodeName;
18892         }
18893         valid = valid && !this.invalidHandleTypes[nodeName];
18894         valid = valid && !this.invalidHandleIds[node.id];
18895
18896         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18897             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18898         }
18899
18900
18901         return valid;
18902
18903     },
18904
18905     /**
18906      * Create the array of horizontal tick marks if an interval was specified
18907      * in setXConstraint().
18908      * @method setXTicks
18909      * @private
18910      */
18911     setXTicks: function(iStartX, iTickSize) {
18912         this.xTicks = [];
18913         this.xTickSize = iTickSize;
18914
18915         var tickMap = {};
18916
18917         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18918             if (!tickMap[i]) {
18919                 this.xTicks[this.xTicks.length] = i;
18920                 tickMap[i] = true;
18921             }
18922         }
18923
18924         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18925             if (!tickMap[i]) {
18926                 this.xTicks[this.xTicks.length] = i;
18927                 tickMap[i] = true;
18928             }
18929         }
18930
18931         this.xTicks.sort(this.DDM.numericSort) ;
18932     },
18933
18934     /**
18935      * Create the array of vertical tick marks if an interval was specified in
18936      * setYConstraint().
18937      * @method setYTicks
18938      * @private
18939      */
18940     setYTicks: function(iStartY, iTickSize) {
18941         this.yTicks = [];
18942         this.yTickSize = iTickSize;
18943
18944         var tickMap = {};
18945
18946         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18947             if (!tickMap[i]) {
18948                 this.yTicks[this.yTicks.length] = i;
18949                 tickMap[i] = true;
18950             }
18951         }
18952
18953         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18954             if (!tickMap[i]) {
18955                 this.yTicks[this.yTicks.length] = i;
18956                 tickMap[i] = true;
18957             }
18958         }
18959
18960         this.yTicks.sort(this.DDM.numericSort) ;
18961     },
18962
18963     /**
18964      * By default, the element can be dragged any place on the screen.  Use
18965      * this method to limit the horizontal travel of the element.  Pass in
18966      * 0,0 for the parameters if you want to lock the drag to the y axis.
18967      * @method setXConstraint
18968      * @param {int} iLeft the number of pixels the element can move to the left
18969      * @param {int} iRight the number of pixels the element can move to the
18970      * right
18971      * @param {int} iTickSize optional parameter for specifying that the
18972      * element
18973      * should move iTickSize pixels at a time.
18974      */
18975     setXConstraint: function(iLeft, iRight, iTickSize) {
18976         this.leftConstraint = iLeft;
18977         this.rightConstraint = iRight;
18978
18979         this.minX = this.initPageX - iLeft;
18980         this.maxX = this.initPageX + iRight;
18981         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18982
18983         this.constrainX = true;
18984     },
18985
18986     /**
18987      * Clears any constraints applied to this instance.  Also clears ticks
18988      * since they can't exist independent of a constraint at this time.
18989      * @method clearConstraints
18990      */
18991     clearConstraints: function() {
18992         this.constrainX = false;
18993         this.constrainY = false;
18994         this.clearTicks();
18995     },
18996
18997     /**
18998      * Clears any tick interval defined for this instance
18999      * @method clearTicks
19000      */
19001     clearTicks: function() {
19002         this.xTicks = null;
19003         this.yTicks = null;
19004         this.xTickSize = 0;
19005         this.yTickSize = 0;
19006     },
19007
19008     /**
19009      * By default, the element can be dragged any place on the screen.  Set
19010      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19011      * parameters if you want to lock the drag to the x axis.
19012      * @method setYConstraint
19013      * @param {int} iUp the number of pixels the element can move up
19014      * @param {int} iDown the number of pixels the element can move down
19015      * @param {int} iTickSize optional parameter for specifying that the
19016      * element should move iTickSize pixels at a time.
19017      */
19018     setYConstraint: function(iUp, iDown, iTickSize) {
19019         this.topConstraint = iUp;
19020         this.bottomConstraint = iDown;
19021
19022         this.minY = this.initPageY - iUp;
19023         this.maxY = this.initPageY + iDown;
19024         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19025
19026         this.constrainY = true;
19027
19028     },
19029
19030     /**
19031      * resetConstraints must be called if you manually reposition a dd element.
19032      * @method resetConstraints
19033      * @param {boolean} maintainOffset
19034      */
19035     resetConstraints: function() {
19036
19037
19038         // Maintain offsets if necessary
19039         if (this.initPageX || this.initPageX === 0) {
19040             // figure out how much this thing has moved
19041             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19042             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19043
19044             this.setInitPosition(dx, dy);
19045
19046         // This is the first time we have detected the element's position
19047         } else {
19048             this.setInitPosition();
19049         }
19050
19051         if (this.constrainX) {
19052             this.setXConstraint( this.leftConstraint,
19053                                  this.rightConstraint,
19054                                  this.xTickSize        );
19055         }
19056
19057         if (this.constrainY) {
19058             this.setYConstraint( this.topConstraint,
19059                                  this.bottomConstraint,
19060                                  this.yTickSize         );
19061         }
19062     },
19063
19064     /**
19065      * Normally the drag element is moved pixel by pixel, but we can specify
19066      * that it move a number of pixels at a time.  This method resolves the
19067      * location when we have it set up like this.
19068      * @method getTick
19069      * @param {int} val where we want to place the object
19070      * @param {int[]} tickArray sorted array of valid points
19071      * @return {int} the closest tick
19072      * @private
19073      */
19074     getTick: function(val, tickArray) {
19075
19076         if (!tickArray) {
19077             // If tick interval is not defined, it is effectively 1 pixel,
19078             // so we return the value passed to us.
19079             return val;
19080         } else if (tickArray[0] >= val) {
19081             // The value is lower than the first tick, so we return the first
19082             // tick.
19083             return tickArray[0];
19084         } else {
19085             for (var i=0, len=tickArray.length; i<len; ++i) {
19086                 var next = i + 1;
19087                 if (tickArray[next] && tickArray[next] >= val) {
19088                     var diff1 = val - tickArray[i];
19089                     var diff2 = tickArray[next] - val;
19090                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19091                 }
19092             }
19093
19094             // The value is larger than the last tick, so we return the last
19095             // tick.
19096             return tickArray[tickArray.length - 1];
19097         }
19098     },
19099
19100     /**
19101      * toString method
19102      * @method toString
19103      * @return {string} string representation of the dd obj
19104      */
19105     toString: function() {
19106         return ("DragDrop " + this.id);
19107     }
19108
19109 });
19110
19111 })();
19112 /*
19113  * Based on:
19114  * Ext JS Library 1.1.1
19115  * Copyright(c) 2006-2007, Ext JS, LLC.
19116  *
19117  * Originally Released Under LGPL - original licence link has changed is not relivant.
19118  *
19119  * Fork - LGPL
19120  * <script type="text/javascript">
19121  */
19122
19123
19124 /**
19125  * The drag and drop utility provides a framework for building drag and drop
19126  * applications.  In addition to enabling drag and drop for specific elements,
19127  * the drag and drop elements are tracked by the manager class, and the
19128  * interactions between the various elements are tracked during the drag and
19129  * the implementing code is notified about these important moments.
19130  */
19131
19132 // Only load the library once.  Rewriting the manager class would orphan
19133 // existing drag and drop instances.
19134 if (!Roo.dd.DragDropMgr) {
19135
19136 /**
19137  * @class Roo.dd.DragDropMgr
19138  * DragDropMgr is a singleton that tracks the element interaction for
19139  * all DragDrop items in the window.  Generally, you will not call
19140  * this class directly, but it does have helper methods that could
19141  * be useful in your DragDrop implementations.
19142  * @singleton
19143  */
19144 Roo.dd.DragDropMgr = function() {
19145
19146     var Event = Roo.EventManager;
19147
19148     return {
19149
19150         /**
19151          * Two dimensional Array of registered DragDrop objects.  The first
19152          * dimension is the DragDrop item group, the second the DragDrop
19153          * object.
19154          * @property ids
19155          * @type {string: string}
19156          * @private
19157          * @static
19158          */
19159         ids: {},
19160
19161         /**
19162          * Array of element ids defined as drag handles.  Used to determine
19163          * if the element that generated the mousedown event is actually the
19164          * handle and not the html element itself.
19165          * @property handleIds
19166          * @type {string: string}
19167          * @private
19168          * @static
19169          */
19170         handleIds: {},
19171
19172         /**
19173          * the DragDrop object that is currently being dragged
19174          * @property dragCurrent
19175          * @type DragDrop
19176          * @private
19177          * @static
19178          **/
19179         dragCurrent: null,
19180
19181         /**
19182          * the DragDrop object(s) that are being hovered over
19183          * @property dragOvers
19184          * @type Array
19185          * @private
19186          * @static
19187          */
19188         dragOvers: {},
19189
19190         /**
19191          * the X distance between the cursor and the object being dragged
19192          * @property deltaX
19193          * @type int
19194          * @private
19195          * @static
19196          */
19197         deltaX: 0,
19198
19199         /**
19200          * the Y distance between the cursor and the object being dragged
19201          * @property deltaY
19202          * @type int
19203          * @private
19204          * @static
19205          */
19206         deltaY: 0,
19207
19208         /**
19209          * Flag to determine if we should prevent the default behavior of the
19210          * events we define. By default this is true, but this can be set to
19211          * false if you need the default behavior (not recommended)
19212          * @property preventDefault
19213          * @type boolean
19214          * @static
19215          */
19216         preventDefault: true,
19217
19218         /**
19219          * Flag to determine if we should stop the propagation of the events
19220          * we generate. This is true by default but you may want to set it to
19221          * false if the html element contains other features that require the
19222          * mouse click.
19223          * @property stopPropagation
19224          * @type boolean
19225          * @static
19226          */
19227         stopPropagation: true,
19228
19229         /**
19230          * Internal flag that is set to true when drag and drop has been
19231          * intialized
19232          * @property initialized
19233          * @private
19234          * @static
19235          */
19236         initalized: false,
19237
19238         /**
19239          * All drag and drop can be disabled.
19240          * @property locked
19241          * @private
19242          * @static
19243          */
19244         locked: false,
19245
19246         /**
19247          * Called the first time an element is registered.
19248          * @method init
19249          * @private
19250          * @static
19251          */
19252         init: function() {
19253             this.initialized = true;
19254         },
19255
19256         /**
19257          * In point mode, drag and drop interaction is defined by the
19258          * location of the cursor during the drag/drop
19259          * @property POINT
19260          * @type int
19261          * @static
19262          */
19263         POINT: 0,
19264
19265         /**
19266          * In intersect mode, drag and drop interactio nis defined by the
19267          * overlap of two or more drag and drop objects.
19268          * @property INTERSECT
19269          * @type int
19270          * @static
19271          */
19272         INTERSECT: 1,
19273
19274         /**
19275          * The current drag and drop mode.  Default: POINT
19276          * @property mode
19277          * @type int
19278          * @static
19279          */
19280         mode: 0,
19281
19282         /**
19283          * Runs method on all drag and drop objects
19284          * @method _execOnAll
19285          * @private
19286          * @static
19287          */
19288         _execOnAll: function(sMethod, args) {
19289             for (var i in this.ids) {
19290                 for (var j in this.ids[i]) {
19291                     var oDD = this.ids[i][j];
19292                     if (! this.isTypeOfDD(oDD)) {
19293                         continue;
19294                     }
19295                     oDD[sMethod].apply(oDD, args);
19296                 }
19297             }
19298         },
19299
19300         /**
19301          * Drag and drop initialization.  Sets up the global event handlers
19302          * @method _onLoad
19303          * @private
19304          * @static
19305          */
19306         _onLoad: function() {
19307
19308             this.init();
19309
19310             if (!Roo.isTouch) {
19311                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19312                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19313             }
19314             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19315             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19316             
19317             Event.on(window,   "unload",    this._onUnload, this, true);
19318             Event.on(window,   "resize",    this._onResize, this, true);
19319             // Event.on(window,   "mouseout",    this._test);
19320
19321         },
19322
19323         /**
19324          * Reset constraints on all drag and drop objs
19325          * @method _onResize
19326          * @private
19327          * @static
19328          */
19329         _onResize: function(e) {
19330             this._execOnAll("resetConstraints", []);
19331         },
19332
19333         /**
19334          * Lock all drag and drop functionality
19335          * @method lock
19336          * @static
19337          */
19338         lock: function() { this.locked = true; },
19339
19340         /**
19341          * Unlock all drag and drop functionality
19342          * @method unlock
19343          * @static
19344          */
19345         unlock: function() { this.locked = false; },
19346
19347         /**
19348          * Is drag and drop locked?
19349          * @method isLocked
19350          * @return {boolean} True if drag and drop is locked, false otherwise.
19351          * @static
19352          */
19353         isLocked: function() { return this.locked; },
19354
19355         /**
19356          * Location cache that is set for all drag drop objects when a drag is
19357          * initiated, cleared when the drag is finished.
19358          * @property locationCache
19359          * @private
19360          * @static
19361          */
19362         locationCache: {},
19363
19364         /**
19365          * Set useCache to false if you want to force object the lookup of each
19366          * drag and drop linked element constantly during a drag.
19367          * @property useCache
19368          * @type boolean
19369          * @static
19370          */
19371         useCache: true,
19372
19373         /**
19374          * The number of pixels that the mouse needs to move after the
19375          * mousedown before the drag is initiated.  Default=3;
19376          * @property clickPixelThresh
19377          * @type int
19378          * @static
19379          */
19380         clickPixelThresh: 3,
19381
19382         /**
19383          * The number of milliseconds after the mousedown event to initiate the
19384          * drag if we don't get a mouseup event. Default=1000
19385          * @property clickTimeThresh
19386          * @type int
19387          * @static
19388          */
19389         clickTimeThresh: 350,
19390
19391         /**
19392          * Flag that indicates that either the drag pixel threshold or the
19393          * mousdown time threshold has been met
19394          * @property dragThreshMet
19395          * @type boolean
19396          * @private
19397          * @static
19398          */
19399         dragThreshMet: false,
19400
19401         /**
19402          * Timeout used for the click time threshold
19403          * @property clickTimeout
19404          * @type Object
19405          * @private
19406          * @static
19407          */
19408         clickTimeout: null,
19409
19410         /**
19411          * The X position of the mousedown event stored for later use when a
19412          * drag threshold is met.
19413          * @property startX
19414          * @type int
19415          * @private
19416          * @static
19417          */
19418         startX: 0,
19419
19420         /**
19421          * The Y position of the mousedown event stored for later use when a
19422          * drag threshold is met.
19423          * @property startY
19424          * @type int
19425          * @private
19426          * @static
19427          */
19428         startY: 0,
19429
19430         /**
19431          * Each DragDrop instance must be registered with the DragDropMgr.
19432          * This is executed in DragDrop.init()
19433          * @method regDragDrop
19434          * @param {DragDrop} oDD the DragDrop object to register
19435          * @param {String} sGroup the name of the group this element belongs to
19436          * @static
19437          */
19438         regDragDrop: function(oDD, sGroup) {
19439             if (!this.initialized) { this.init(); }
19440
19441             if (!this.ids[sGroup]) {
19442                 this.ids[sGroup] = {};
19443             }
19444             this.ids[sGroup][oDD.id] = oDD;
19445         },
19446
19447         /**
19448          * Removes the supplied dd instance from the supplied group. Executed
19449          * by DragDrop.removeFromGroup, so don't call this function directly.
19450          * @method removeDDFromGroup
19451          * @private
19452          * @static
19453          */
19454         removeDDFromGroup: function(oDD, sGroup) {
19455             if (!this.ids[sGroup]) {
19456                 this.ids[sGroup] = {};
19457             }
19458
19459             var obj = this.ids[sGroup];
19460             if (obj && obj[oDD.id]) {
19461                 delete obj[oDD.id];
19462             }
19463         },
19464
19465         /**
19466          * Unregisters a drag and drop item.  This is executed in
19467          * DragDrop.unreg, use that method instead of calling this directly.
19468          * @method _remove
19469          * @private
19470          * @static
19471          */
19472         _remove: function(oDD) {
19473             for (var g in oDD.groups) {
19474                 if (g && this.ids[g][oDD.id]) {
19475                     delete this.ids[g][oDD.id];
19476                 }
19477             }
19478             delete this.handleIds[oDD.id];
19479         },
19480
19481         /**
19482          * Each DragDrop handle element must be registered.  This is done
19483          * automatically when executing DragDrop.setHandleElId()
19484          * @method regHandle
19485          * @param {String} sDDId the DragDrop id this element is a handle for
19486          * @param {String} sHandleId the id of the element that is the drag
19487          * handle
19488          * @static
19489          */
19490         regHandle: function(sDDId, sHandleId) {
19491             if (!this.handleIds[sDDId]) {
19492                 this.handleIds[sDDId] = {};
19493             }
19494             this.handleIds[sDDId][sHandleId] = sHandleId;
19495         },
19496
19497         /**
19498          * Utility function to determine if a given element has been
19499          * registered as a drag drop item.
19500          * @method isDragDrop
19501          * @param {String} id the element id to check
19502          * @return {boolean} true if this element is a DragDrop item,
19503          * false otherwise
19504          * @static
19505          */
19506         isDragDrop: function(id) {
19507             return ( this.getDDById(id) ) ? true : false;
19508         },
19509
19510         /**
19511          * Returns the drag and drop instances that are in all groups the
19512          * passed in instance belongs to.
19513          * @method getRelated
19514          * @param {DragDrop} p_oDD the obj to get related data for
19515          * @param {boolean} bTargetsOnly if true, only return targetable objs
19516          * @return {DragDrop[]} the related instances
19517          * @static
19518          */
19519         getRelated: function(p_oDD, bTargetsOnly) {
19520             var oDDs = [];
19521             for (var i in p_oDD.groups) {
19522                 for (j in this.ids[i]) {
19523                     var dd = this.ids[i][j];
19524                     if (! this.isTypeOfDD(dd)) {
19525                         continue;
19526                     }
19527                     if (!bTargetsOnly || dd.isTarget) {
19528                         oDDs[oDDs.length] = dd;
19529                     }
19530                 }
19531             }
19532
19533             return oDDs;
19534         },
19535
19536         /**
19537          * Returns true if the specified dd target is a legal target for
19538          * the specifice drag obj
19539          * @method isLegalTarget
19540          * @param {DragDrop} the drag obj
19541          * @param {DragDrop} the target
19542          * @return {boolean} true if the target is a legal target for the
19543          * dd obj
19544          * @static
19545          */
19546         isLegalTarget: function (oDD, oTargetDD) {
19547             var targets = this.getRelated(oDD, true);
19548             for (var i=0, len=targets.length;i<len;++i) {
19549                 if (targets[i].id == oTargetDD.id) {
19550                     return true;
19551                 }
19552             }
19553
19554             return false;
19555         },
19556
19557         /**
19558          * My goal is to be able to transparently determine if an object is
19559          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19560          * returns "object", oDD.constructor.toString() always returns
19561          * "DragDrop" and not the name of the subclass.  So for now it just
19562          * evaluates a well-known variable in DragDrop.
19563          * @method isTypeOfDD
19564          * @param {Object} the object to evaluate
19565          * @return {boolean} true if typeof oDD = DragDrop
19566          * @static
19567          */
19568         isTypeOfDD: function (oDD) {
19569             return (oDD && oDD.__ygDragDrop);
19570         },
19571
19572         /**
19573          * Utility function to determine if a given element has been
19574          * registered as a drag drop handle for the given Drag Drop object.
19575          * @method isHandle
19576          * @param {String} id the element id to check
19577          * @return {boolean} true if this element is a DragDrop handle, false
19578          * otherwise
19579          * @static
19580          */
19581         isHandle: function(sDDId, sHandleId) {
19582             return ( this.handleIds[sDDId] &&
19583                             this.handleIds[sDDId][sHandleId] );
19584         },
19585
19586         /**
19587          * Returns the DragDrop instance for a given id
19588          * @method getDDById
19589          * @param {String} id the id of the DragDrop object
19590          * @return {DragDrop} the drag drop object, null if it is not found
19591          * @static
19592          */
19593         getDDById: function(id) {
19594             for (var i in this.ids) {
19595                 if (this.ids[i][id]) {
19596                     return this.ids[i][id];
19597                 }
19598             }
19599             return null;
19600         },
19601
19602         /**
19603          * Fired after a registered DragDrop object gets the mousedown event.
19604          * Sets up the events required to track the object being dragged
19605          * @method handleMouseDown
19606          * @param {Event} e the event
19607          * @param oDD the DragDrop object being dragged
19608          * @private
19609          * @static
19610          */
19611         handleMouseDown: function(e, oDD) {
19612             if(Roo.QuickTips){
19613                 Roo.QuickTips.disable();
19614             }
19615             this.currentTarget = e.getTarget();
19616
19617             this.dragCurrent = oDD;
19618
19619             var el = oDD.getEl();
19620
19621             // track start position
19622             this.startX = e.getPageX();
19623             this.startY = e.getPageY();
19624
19625             this.deltaX = this.startX - el.offsetLeft;
19626             this.deltaY = this.startY - el.offsetTop;
19627
19628             this.dragThreshMet = false;
19629
19630             this.clickTimeout = setTimeout(
19631                     function() {
19632                         var DDM = Roo.dd.DDM;
19633                         DDM.startDrag(DDM.startX, DDM.startY);
19634                     },
19635                     this.clickTimeThresh );
19636         },
19637
19638         /**
19639          * Fired when either the drag pixel threshol or the mousedown hold
19640          * time threshold has been met.
19641          * @method startDrag
19642          * @param x {int} the X position of the original mousedown
19643          * @param y {int} the Y position of the original mousedown
19644          * @static
19645          */
19646         startDrag: function(x, y) {
19647             clearTimeout(this.clickTimeout);
19648             if (this.dragCurrent) {
19649                 this.dragCurrent.b4StartDrag(x, y);
19650                 this.dragCurrent.startDrag(x, y);
19651             }
19652             this.dragThreshMet = true;
19653         },
19654
19655         /**
19656          * Internal function to handle the mouseup event.  Will be invoked
19657          * from the context of the document.
19658          * @method handleMouseUp
19659          * @param {Event} e the event
19660          * @private
19661          * @static
19662          */
19663         handleMouseUp: function(e) {
19664
19665             if(Roo.QuickTips){
19666                 Roo.QuickTips.enable();
19667             }
19668             if (! this.dragCurrent) {
19669                 return;
19670             }
19671
19672             clearTimeout(this.clickTimeout);
19673
19674             if (this.dragThreshMet) {
19675                 this.fireEvents(e, true);
19676             } else {
19677             }
19678
19679             this.stopDrag(e);
19680
19681             this.stopEvent(e);
19682         },
19683
19684         /**
19685          * Utility to stop event propagation and event default, if these
19686          * features are turned on.
19687          * @method stopEvent
19688          * @param {Event} e the event as returned by this.getEvent()
19689          * @static
19690          */
19691         stopEvent: function(e){
19692             if(this.stopPropagation) {
19693                 e.stopPropagation();
19694             }
19695
19696             if (this.preventDefault) {
19697                 e.preventDefault();
19698             }
19699         },
19700
19701         /**
19702          * Internal function to clean up event handlers after the drag
19703          * operation is complete
19704          * @method stopDrag
19705          * @param {Event} e the event
19706          * @private
19707          * @static
19708          */
19709         stopDrag: function(e) {
19710             // Fire the drag end event for the item that was dragged
19711             if (this.dragCurrent) {
19712                 if (this.dragThreshMet) {
19713                     this.dragCurrent.b4EndDrag(e);
19714                     this.dragCurrent.endDrag(e);
19715                 }
19716
19717                 this.dragCurrent.onMouseUp(e);
19718             }
19719
19720             this.dragCurrent = null;
19721             this.dragOvers = {};
19722         },
19723
19724         /**
19725          * Internal function to handle the mousemove event.  Will be invoked
19726          * from the context of the html element.
19727          *
19728          * @TODO figure out what we can do about mouse events lost when the
19729          * user drags objects beyond the window boundary.  Currently we can
19730          * detect this in internet explorer by verifying that the mouse is
19731          * down during the mousemove event.  Firefox doesn't give us the
19732          * button state on the mousemove event.
19733          * @method handleMouseMove
19734          * @param {Event} e the event
19735          * @private
19736          * @static
19737          */
19738         handleMouseMove: function(e) {
19739             if (! this.dragCurrent) {
19740                 return true;
19741             }
19742
19743             // var button = e.which || e.button;
19744
19745             // check for IE mouseup outside of page boundary
19746             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19747                 this.stopEvent(e);
19748                 return this.handleMouseUp(e);
19749             }
19750
19751             if (!this.dragThreshMet) {
19752                 var diffX = Math.abs(this.startX - e.getPageX());
19753                 var diffY = Math.abs(this.startY - e.getPageY());
19754                 if (diffX > this.clickPixelThresh ||
19755                             diffY > this.clickPixelThresh) {
19756                     this.startDrag(this.startX, this.startY);
19757                 }
19758             }
19759
19760             if (this.dragThreshMet) {
19761                 this.dragCurrent.b4Drag(e);
19762                 this.dragCurrent.onDrag(e);
19763                 if(!this.dragCurrent.moveOnly){
19764                     this.fireEvents(e, false);
19765                 }
19766             }
19767
19768             this.stopEvent(e);
19769
19770             return true;
19771         },
19772
19773         /**
19774          * Iterates over all of the DragDrop elements to find ones we are
19775          * hovering over or dropping on
19776          * @method fireEvents
19777          * @param {Event} e the event
19778          * @param {boolean} isDrop is this a drop op or a mouseover op?
19779          * @private
19780          * @static
19781          */
19782         fireEvents: function(e, isDrop) {
19783             var dc = this.dragCurrent;
19784
19785             // If the user did the mouse up outside of the window, we could
19786             // get here even though we have ended the drag.
19787             if (!dc || dc.isLocked()) {
19788                 return;
19789             }
19790
19791             var pt = e.getPoint();
19792
19793             // cache the previous dragOver array
19794             var oldOvers = [];
19795
19796             var outEvts   = [];
19797             var overEvts  = [];
19798             var dropEvts  = [];
19799             var enterEvts = [];
19800
19801             // Check to see if the object(s) we were hovering over is no longer
19802             // being hovered over so we can fire the onDragOut event
19803             for (var i in this.dragOvers) {
19804
19805                 var ddo = this.dragOvers[i];
19806
19807                 if (! this.isTypeOfDD(ddo)) {
19808                     continue;
19809                 }
19810
19811                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19812                     outEvts.push( ddo );
19813                 }
19814
19815                 oldOvers[i] = true;
19816                 delete this.dragOvers[i];
19817             }
19818
19819             for (var sGroup in dc.groups) {
19820
19821                 if ("string" != typeof sGroup) {
19822                     continue;
19823                 }
19824
19825                 for (i in this.ids[sGroup]) {
19826                     var oDD = this.ids[sGroup][i];
19827                     if (! this.isTypeOfDD(oDD)) {
19828                         continue;
19829                     }
19830
19831                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19832                         if (this.isOverTarget(pt, oDD, this.mode)) {
19833                             // look for drop interactions
19834                             if (isDrop) {
19835                                 dropEvts.push( oDD );
19836                             // look for drag enter and drag over interactions
19837                             } else {
19838
19839                                 // initial drag over: dragEnter fires
19840                                 if (!oldOvers[oDD.id]) {
19841                                     enterEvts.push( oDD );
19842                                 // subsequent drag overs: dragOver fires
19843                                 } else {
19844                                     overEvts.push( oDD );
19845                                 }
19846
19847                                 this.dragOvers[oDD.id] = oDD;
19848                             }
19849                         }
19850                     }
19851                 }
19852             }
19853
19854             if (this.mode) {
19855                 if (outEvts.length) {
19856                     dc.b4DragOut(e, outEvts);
19857                     dc.onDragOut(e, outEvts);
19858                 }
19859
19860                 if (enterEvts.length) {
19861                     dc.onDragEnter(e, enterEvts);
19862                 }
19863
19864                 if (overEvts.length) {
19865                     dc.b4DragOver(e, overEvts);
19866                     dc.onDragOver(e, overEvts);
19867                 }
19868
19869                 if (dropEvts.length) {
19870                     dc.b4DragDrop(e, dropEvts);
19871                     dc.onDragDrop(e, dropEvts);
19872                 }
19873
19874             } else {
19875                 // fire dragout events
19876                 var len = 0;
19877                 for (i=0, len=outEvts.length; i<len; ++i) {
19878                     dc.b4DragOut(e, outEvts[i].id);
19879                     dc.onDragOut(e, outEvts[i].id);
19880                 }
19881
19882                 // fire enter events
19883                 for (i=0,len=enterEvts.length; i<len; ++i) {
19884                     // dc.b4DragEnter(e, oDD.id);
19885                     dc.onDragEnter(e, enterEvts[i].id);
19886                 }
19887
19888                 // fire over events
19889                 for (i=0,len=overEvts.length; i<len; ++i) {
19890                     dc.b4DragOver(e, overEvts[i].id);
19891                     dc.onDragOver(e, overEvts[i].id);
19892                 }
19893
19894                 // fire drop events
19895                 for (i=0, len=dropEvts.length; i<len; ++i) {
19896                     dc.b4DragDrop(e, dropEvts[i].id);
19897                     dc.onDragDrop(e, dropEvts[i].id);
19898                 }
19899
19900             }
19901
19902             // notify about a drop that did not find a target
19903             if (isDrop && !dropEvts.length) {
19904                 dc.onInvalidDrop(e);
19905             }
19906
19907         },
19908
19909         /**
19910          * Helper function for getting the best match from the list of drag
19911          * and drop objects returned by the drag and drop events when we are
19912          * in INTERSECT mode.  It returns either the first object that the
19913          * cursor is over, or the object that has the greatest overlap with
19914          * the dragged element.
19915          * @method getBestMatch
19916          * @param  {DragDrop[]} dds The array of drag and drop objects
19917          * targeted
19918          * @return {DragDrop}       The best single match
19919          * @static
19920          */
19921         getBestMatch: function(dds) {
19922             var winner = null;
19923             // Return null if the input is not what we expect
19924             //if (!dds || !dds.length || dds.length == 0) {
19925                // winner = null;
19926             // If there is only one item, it wins
19927             //} else if (dds.length == 1) {
19928
19929             var len = dds.length;
19930
19931             if (len == 1) {
19932                 winner = dds[0];
19933             } else {
19934                 // Loop through the targeted items
19935                 for (var i=0; i<len; ++i) {
19936                     var dd = dds[i];
19937                     // If the cursor is over the object, it wins.  If the
19938                     // cursor is over multiple matches, the first one we come
19939                     // to wins.
19940                     if (dd.cursorIsOver) {
19941                         winner = dd;
19942                         break;
19943                     // Otherwise the object with the most overlap wins
19944                     } else {
19945                         if (!winner ||
19946                             winner.overlap.getArea() < dd.overlap.getArea()) {
19947                             winner = dd;
19948                         }
19949                     }
19950                 }
19951             }
19952
19953             return winner;
19954         },
19955
19956         /**
19957          * Refreshes the cache of the top-left and bottom-right points of the
19958          * drag and drop objects in the specified group(s).  This is in the
19959          * format that is stored in the drag and drop instance, so typical
19960          * usage is:
19961          * <code>
19962          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19963          * </code>
19964          * Alternatively:
19965          * <code>
19966          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19967          * </code>
19968          * @TODO this really should be an indexed array.  Alternatively this
19969          * method could accept both.
19970          * @method refreshCache
19971          * @param {Object} groups an associative array of groups to refresh
19972          * @static
19973          */
19974         refreshCache: function(groups) {
19975             for (var sGroup in groups) {
19976                 if ("string" != typeof sGroup) {
19977                     continue;
19978                 }
19979                 for (var i in this.ids[sGroup]) {
19980                     var oDD = this.ids[sGroup][i];
19981
19982                     if (this.isTypeOfDD(oDD)) {
19983                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19984                         var loc = this.getLocation(oDD);
19985                         if (loc) {
19986                             this.locationCache[oDD.id] = loc;
19987                         } else {
19988                             delete this.locationCache[oDD.id];
19989                             // this will unregister the drag and drop object if
19990                             // the element is not in a usable state
19991                             // oDD.unreg();
19992                         }
19993                     }
19994                 }
19995             }
19996         },
19997
19998         /**
19999          * This checks to make sure an element exists and is in the DOM.  The
20000          * main purpose is to handle cases where innerHTML is used to remove
20001          * drag and drop objects from the DOM.  IE provides an 'unspecified
20002          * error' when trying to access the offsetParent of such an element
20003          * @method verifyEl
20004          * @param {HTMLElement} el the element to check
20005          * @return {boolean} true if the element looks usable
20006          * @static
20007          */
20008         verifyEl: function(el) {
20009             if (el) {
20010                 var parent;
20011                 if(Roo.isIE){
20012                     try{
20013                         parent = el.offsetParent;
20014                     }catch(e){}
20015                 }else{
20016                     parent = el.offsetParent;
20017                 }
20018                 if (parent) {
20019                     return true;
20020                 }
20021             }
20022
20023             return false;
20024         },
20025
20026         /**
20027          * Returns a Region object containing the drag and drop element's position
20028          * and size, including the padding configured for it
20029          * @method getLocation
20030          * @param {DragDrop} oDD the drag and drop object to get the
20031          *                       location for
20032          * @return {Roo.lib.Region} a Region object representing the total area
20033          *                             the element occupies, including any padding
20034          *                             the instance is configured for.
20035          * @static
20036          */
20037         getLocation: function(oDD) {
20038             if (! this.isTypeOfDD(oDD)) {
20039                 return null;
20040             }
20041
20042             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20043
20044             try {
20045                 pos= Roo.lib.Dom.getXY(el);
20046             } catch (e) { }
20047
20048             if (!pos) {
20049                 return null;
20050             }
20051
20052             x1 = pos[0];
20053             x2 = x1 + el.offsetWidth;
20054             y1 = pos[1];
20055             y2 = y1 + el.offsetHeight;
20056
20057             t = y1 - oDD.padding[0];
20058             r = x2 + oDD.padding[1];
20059             b = y2 + oDD.padding[2];
20060             l = x1 - oDD.padding[3];
20061
20062             return new Roo.lib.Region( t, r, b, l );
20063         },
20064
20065         /**
20066          * Checks the cursor location to see if it over the target
20067          * @method isOverTarget
20068          * @param {Roo.lib.Point} pt The point to evaluate
20069          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20070          * @return {boolean} true if the mouse is over the target
20071          * @private
20072          * @static
20073          */
20074         isOverTarget: function(pt, oTarget, intersect) {
20075             // use cache if available
20076             var loc = this.locationCache[oTarget.id];
20077             if (!loc || !this.useCache) {
20078                 loc = this.getLocation(oTarget);
20079                 this.locationCache[oTarget.id] = loc;
20080
20081             }
20082
20083             if (!loc) {
20084                 return false;
20085             }
20086
20087             oTarget.cursorIsOver = loc.contains( pt );
20088
20089             // DragDrop is using this as a sanity check for the initial mousedown
20090             // in this case we are done.  In POINT mode, if the drag obj has no
20091             // contraints, we are also done. Otherwise we need to evaluate the
20092             // location of the target as related to the actual location of the
20093             // dragged element.
20094             var dc = this.dragCurrent;
20095             if (!dc || !dc.getTargetCoord ||
20096                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20097                 return oTarget.cursorIsOver;
20098             }
20099
20100             oTarget.overlap = null;
20101
20102             // Get the current location of the drag element, this is the
20103             // location of the mouse event less the delta that represents
20104             // where the original mousedown happened on the element.  We
20105             // need to consider constraints and ticks as well.
20106             var pos = dc.getTargetCoord(pt.x, pt.y);
20107
20108             var el = dc.getDragEl();
20109             var curRegion = new Roo.lib.Region( pos.y,
20110                                                    pos.x + el.offsetWidth,
20111                                                    pos.y + el.offsetHeight,
20112                                                    pos.x );
20113
20114             var overlap = curRegion.intersect(loc);
20115
20116             if (overlap) {
20117                 oTarget.overlap = overlap;
20118                 return (intersect) ? true : oTarget.cursorIsOver;
20119             } else {
20120                 return false;
20121             }
20122         },
20123
20124         /**
20125          * unload event handler
20126          * @method _onUnload
20127          * @private
20128          * @static
20129          */
20130         _onUnload: function(e, me) {
20131             Roo.dd.DragDropMgr.unregAll();
20132         },
20133
20134         /**
20135          * Cleans up the drag and drop events and objects.
20136          * @method unregAll
20137          * @private
20138          * @static
20139          */
20140         unregAll: function() {
20141
20142             if (this.dragCurrent) {
20143                 this.stopDrag();
20144                 this.dragCurrent = null;
20145             }
20146
20147             this._execOnAll("unreg", []);
20148
20149             for (i in this.elementCache) {
20150                 delete this.elementCache[i];
20151             }
20152
20153             this.elementCache = {};
20154             this.ids = {};
20155         },
20156
20157         /**
20158          * A cache of DOM elements
20159          * @property elementCache
20160          * @private
20161          * @static
20162          */
20163         elementCache: {},
20164
20165         /**
20166          * Get the wrapper for the DOM element specified
20167          * @method getElWrapper
20168          * @param {String} id the id of the element to get
20169          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20170          * @private
20171          * @deprecated This wrapper isn't that useful
20172          * @static
20173          */
20174         getElWrapper: function(id) {
20175             var oWrapper = this.elementCache[id];
20176             if (!oWrapper || !oWrapper.el) {
20177                 oWrapper = this.elementCache[id] =
20178                     new this.ElementWrapper(Roo.getDom(id));
20179             }
20180             return oWrapper;
20181         },
20182
20183         /**
20184          * Returns the actual DOM element
20185          * @method getElement
20186          * @param {String} id the id of the elment to get
20187          * @return {Object} The element
20188          * @deprecated use Roo.getDom instead
20189          * @static
20190          */
20191         getElement: function(id) {
20192             return Roo.getDom(id);
20193         },
20194
20195         /**
20196          * Returns the style property for the DOM element (i.e.,
20197          * document.getElById(id).style)
20198          * @method getCss
20199          * @param {String} id the id of the elment to get
20200          * @return {Object} The style property of the element
20201          * @deprecated use Roo.getDom instead
20202          * @static
20203          */
20204         getCss: function(id) {
20205             var el = Roo.getDom(id);
20206             return (el) ? el.style : null;
20207         },
20208
20209         /**
20210          * Inner class for cached elements
20211          * @class DragDropMgr.ElementWrapper
20212          * @for DragDropMgr
20213          * @private
20214          * @deprecated
20215          */
20216         ElementWrapper: function(el) {
20217                 /**
20218                  * The element
20219                  * @property el
20220                  */
20221                 this.el = el || null;
20222                 /**
20223                  * The element id
20224                  * @property id
20225                  */
20226                 this.id = this.el && el.id;
20227                 /**
20228                  * A reference to the style property
20229                  * @property css
20230                  */
20231                 this.css = this.el && el.style;
20232             },
20233
20234         /**
20235          * Returns the X position of an html element
20236          * @method getPosX
20237          * @param el the element for which to get the position
20238          * @return {int} the X coordinate
20239          * @for DragDropMgr
20240          * @deprecated use Roo.lib.Dom.getX instead
20241          * @static
20242          */
20243         getPosX: function(el) {
20244             return Roo.lib.Dom.getX(el);
20245         },
20246
20247         /**
20248          * Returns the Y position of an html element
20249          * @method getPosY
20250          * @param el the element for which to get the position
20251          * @return {int} the Y coordinate
20252          * @deprecated use Roo.lib.Dom.getY instead
20253          * @static
20254          */
20255         getPosY: function(el) {
20256             return Roo.lib.Dom.getY(el);
20257         },
20258
20259         /**
20260          * Swap two nodes.  In IE, we use the native method, for others we
20261          * emulate the IE behavior
20262          * @method swapNode
20263          * @param n1 the first node to swap
20264          * @param n2 the other node to swap
20265          * @static
20266          */
20267         swapNode: function(n1, n2) {
20268             if (n1.swapNode) {
20269                 n1.swapNode(n2);
20270             } else {
20271                 var p = n2.parentNode;
20272                 var s = n2.nextSibling;
20273
20274                 if (s == n1) {
20275                     p.insertBefore(n1, n2);
20276                 } else if (n2 == n1.nextSibling) {
20277                     p.insertBefore(n2, n1);
20278                 } else {
20279                     n1.parentNode.replaceChild(n2, n1);
20280                     p.insertBefore(n1, s);
20281                 }
20282             }
20283         },
20284
20285         /**
20286          * Returns the current scroll position
20287          * @method getScroll
20288          * @private
20289          * @static
20290          */
20291         getScroll: function () {
20292             var t, l, dde=document.documentElement, db=document.body;
20293             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20294                 t = dde.scrollTop;
20295                 l = dde.scrollLeft;
20296             } else if (db) {
20297                 t = db.scrollTop;
20298                 l = db.scrollLeft;
20299             } else {
20300
20301             }
20302             return { top: t, left: l };
20303         },
20304
20305         /**
20306          * Returns the specified element style property
20307          * @method getStyle
20308          * @param {HTMLElement} el          the element
20309          * @param {string}      styleProp   the style property
20310          * @return {string} The value of the style property
20311          * @deprecated use Roo.lib.Dom.getStyle
20312          * @static
20313          */
20314         getStyle: function(el, styleProp) {
20315             return Roo.fly(el).getStyle(styleProp);
20316         },
20317
20318         /**
20319          * Gets the scrollTop
20320          * @method getScrollTop
20321          * @return {int} the document's scrollTop
20322          * @static
20323          */
20324         getScrollTop: function () { return this.getScroll().top; },
20325
20326         /**
20327          * Gets the scrollLeft
20328          * @method getScrollLeft
20329          * @return {int} the document's scrollTop
20330          * @static
20331          */
20332         getScrollLeft: function () { return this.getScroll().left; },
20333
20334         /**
20335          * Sets the x/y position of an element to the location of the
20336          * target element.
20337          * @method moveToEl
20338          * @param {HTMLElement} moveEl      The element to move
20339          * @param {HTMLElement} targetEl    The position reference element
20340          * @static
20341          */
20342         moveToEl: function (moveEl, targetEl) {
20343             var aCoord = Roo.lib.Dom.getXY(targetEl);
20344             Roo.lib.Dom.setXY(moveEl, aCoord);
20345         },
20346
20347         /**
20348          * Numeric array sort function
20349          * @method numericSort
20350          * @static
20351          */
20352         numericSort: function(a, b) { return (a - b); },
20353
20354         /**
20355          * Internal counter
20356          * @property _timeoutCount
20357          * @private
20358          * @static
20359          */
20360         _timeoutCount: 0,
20361
20362         /**
20363          * Trying to make the load order less important.  Without this we get
20364          * an error if this file is loaded before the Event Utility.
20365          * @method _addListeners
20366          * @private
20367          * @static
20368          */
20369         _addListeners: function() {
20370             var DDM = Roo.dd.DDM;
20371             if ( Roo.lib.Event && document ) {
20372                 DDM._onLoad();
20373             } else {
20374                 if (DDM._timeoutCount > 2000) {
20375                 } else {
20376                     setTimeout(DDM._addListeners, 10);
20377                     if (document && document.body) {
20378                         DDM._timeoutCount += 1;
20379                     }
20380                 }
20381             }
20382         },
20383
20384         /**
20385          * Recursively searches the immediate parent and all child nodes for
20386          * the handle element in order to determine wheter or not it was
20387          * clicked.
20388          * @method handleWasClicked
20389          * @param node the html element to inspect
20390          * @static
20391          */
20392         handleWasClicked: function(node, id) {
20393             if (this.isHandle(id, node.id)) {
20394                 return true;
20395             } else {
20396                 // check to see if this is a text node child of the one we want
20397                 var p = node.parentNode;
20398
20399                 while (p) {
20400                     if (this.isHandle(id, p.id)) {
20401                         return true;
20402                     } else {
20403                         p = p.parentNode;
20404                     }
20405                 }
20406             }
20407
20408             return false;
20409         }
20410
20411     };
20412
20413 }();
20414
20415 // shorter alias, save a few bytes
20416 Roo.dd.DDM = Roo.dd.DragDropMgr;
20417 Roo.dd.DDM._addListeners();
20418
20419 }/*
20420  * Based on:
20421  * Ext JS Library 1.1.1
20422  * Copyright(c) 2006-2007, Ext JS, LLC.
20423  *
20424  * Originally Released Under LGPL - original licence link has changed is not relivant.
20425  *
20426  * Fork - LGPL
20427  * <script type="text/javascript">
20428  */
20429
20430 /**
20431  * @class Roo.dd.DD
20432  * A DragDrop implementation where the linked element follows the
20433  * mouse cursor during a drag.
20434  * @extends Roo.dd.DragDrop
20435  * @constructor
20436  * @param {String} id the id of the linked element
20437  * @param {String} sGroup the group of related DragDrop items
20438  * @param {object} config an object containing configurable attributes
20439  *                Valid properties for DD:
20440  *                    scroll
20441  */
20442 Roo.dd.DD = function(id, sGroup, config) {
20443     if (id) {
20444         this.init(id, sGroup, config);
20445     }
20446 };
20447
20448 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20449
20450     /**
20451      * When set to true, the utility automatically tries to scroll the browser
20452      * window wehn a drag and drop element is dragged near the viewport boundary.
20453      * Defaults to true.
20454      * @property scroll
20455      * @type boolean
20456      */
20457     scroll: true,
20458
20459     /**
20460      * Sets the pointer offset to the distance between the linked element's top
20461      * left corner and the location the element was clicked
20462      * @method autoOffset
20463      * @param {int} iPageX the X coordinate of the click
20464      * @param {int} iPageY the Y coordinate of the click
20465      */
20466     autoOffset: function(iPageX, iPageY) {
20467         var x = iPageX - this.startPageX;
20468         var y = iPageY - this.startPageY;
20469         this.setDelta(x, y);
20470     },
20471
20472     /**
20473      * Sets the pointer offset.  You can call this directly to force the
20474      * offset to be in a particular location (e.g., pass in 0,0 to set it
20475      * to the center of the object)
20476      * @method setDelta
20477      * @param {int} iDeltaX the distance from the left
20478      * @param {int} iDeltaY the distance from the top
20479      */
20480     setDelta: function(iDeltaX, iDeltaY) {
20481         this.deltaX = iDeltaX;
20482         this.deltaY = iDeltaY;
20483     },
20484
20485     /**
20486      * Sets the drag element to the location of the mousedown or click event,
20487      * maintaining the cursor location relative to the location on the element
20488      * that was clicked.  Override this if you want to place the element in a
20489      * location other than where the cursor is.
20490      * @method setDragElPos
20491      * @param {int} iPageX the X coordinate of the mousedown or drag event
20492      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20493      */
20494     setDragElPos: function(iPageX, iPageY) {
20495         // the first time we do this, we are going to check to make sure
20496         // the element has css positioning
20497
20498         var el = this.getDragEl();
20499         this.alignElWithMouse(el, iPageX, iPageY);
20500     },
20501
20502     /**
20503      * Sets the element to the location of the mousedown or click event,
20504      * maintaining the cursor location relative to the location on the element
20505      * that was clicked.  Override this if you want to place the element in a
20506      * location other than where the cursor is.
20507      * @method alignElWithMouse
20508      * @param {HTMLElement} el the element to move
20509      * @param {int} iPageX the X coordinate of the mousedown or drag event
20510      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20511      */
20512     alignElWithMouse: function(el, iPageX, iPageY) {
20513         var oCoord = this.getTargetCoord(iPageX, iPageY);
20514         var fly = el.dom ? el : Roo.fly(el);
20515         if (!this.deltaSetXY) {
20516             var aCoord = [oCoord.x, oCoord.y];
20517             fly.setXY(aCoord);
20518             var newLeft = fly.getLeft(true);
20519             var newTop  = fly.getTop(true);
20520             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20521         } else {
20522             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20523         }
20524
20525         this.cachePosition(oCoord.x, oCoord.y);
20526         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20527         return oCoord;
20528     },
20529
20530     /**
20531      * Saves the most recent position so that we can reset the constraints and
20532      * tick marks on-demand.  We need to know this so that we can calculate the
20533      * number of pixels the element is offset from its original position.
20534      * @method cachePosition
20535      * @param iPageX the current x position (optional, this just makes it so we
20536      * don't have to look it up again)
20537      * @param iPageY the current y position (optional, this just makes it so we
20538      * don't have to look it up again)
20539      */
20540     cachePosition: function(iPageX, iPageY) {
20541         if (iPageX) {
20542             this.lastPageX = iPageX;
20543             this.lastPageY = iPageY;
20544         } else {
20545             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20546             this.lastPageX = aCoord[0];
20547             this.lastPageY = aCoord[1];
20548         }
20549     },
20550
20551     /**
20552      * Auto-scroll the window if the dragged object has been moved beyond the
20553      * visible window boundary.
20554      * @method autoScroll
20555      * @param {int} x the drag element's x position
20556      * @param {int} y the drag element's y position
20557      * @param {int} h the height of the drag element
20558      * @param {int} w the width of the drag element
20559      * @private
20560      */
20561     autoScroll: function(x, y, h, w) {
20562
20563         if (this.scroll) {
20564             // The client height
20565             var clientH = Roo.lib.Dom.getViewWidth();
20566
20567             // The client width
20568             var clientW = Roo.lib.Dom.getViewHeight();
20569
20570             // The amt scrolled down
20571             var st = this.DDM.getScrollTop();
20572
20573             // The amt scrolled right
20574             var sl = this.DDM.getScrollLeft();
20575
20576             // Location of the bottom of the element
20577             var bot = h + y;
20578
20579             // Location of the right of the element
20580             var right = w + x;
20581
20582             // The distance from the cursor to the bottom of the visible area,
20583             // adjusted so that we don't scroll if the cursor is beyond the
20584             // element drag constraints
20585             var toBot = (clientH + st - y - this.deltaY);
20586
20587             // The distance from the cursor to the right of the visible area
20588             var toRight = (clientW + sl - x - this.deltaX);
20589
20590
20591             // How close to the edge the cursor must be before we scroll
20592             // var thresh = (document.all) ? 100 : 40;
20593             var thresh = 40;
20594
20595             // How many pixels to scroll per autoscroll op.  This helps to reduce
20596             // clunky scrolling. IE is more sensitive about this ... it needs this
20597             // value to be higher.
20598             var scrAmt = (document.all) ? 80 : 30;
20599
20600             // Scroll down if we are near the bottom of the visible page and the
20601             // obj extends below the crease
20602             if ( bot > clientH && toBot < thresh ) {
20603                 window.scrollTo(sl, st + scrAmt);
20604             }
20605
20606             // Scroll up if the window is scrolled down and the top of the object
20607             // goes above the top border
20608             if ( y < st && st > 0 && y - st < thresh ) {
20609                 window.scrollTo(sl, st - scrAmt);
20610             }
20611
20612             // Scroll right if the obj is beyond the right border and the cursor is
20613             // near the border.
20614             if ( right > clientW && toRight < thresh ) {
20615                 window.scrollTo(sl + scrAmt, st);
20616             }
20617
20618             // Scroll left if the window has been scrolled to the right and the obj
20619             // extends past the left border
20620             if ( x < sl && sl > 0 && x - sl < thresh ) {
20621                 window.scrollTo(sl - scrAmt, st);
20622             }
20623         }
20624     },
20625
20626     /**
20627      * Finds the location the element should be placed if we want to move
20628      * it to where the mouse location less the click offset would place us.
20629      * @method getTargetCoord
20630      * @param {int} iPageX the X coordinate of the click
20631      * @param {int} iPageY the Y coordinate of the click
20632      * @return an object that contains the coordinates (Object.x and Object.y)
20633      * @private
20634      */
20635     getTargetCoord: function(iPageX, iPageY) {
20636
20637
20638         var x = iPageX - this.deltaX;
20639         var y = iPageY - this.deltaY;
20640
20641         if (this.constrainX) {
20642             if (x < this.minX) { x = this.minX; }
20643             if (x > this.maxX) { x = this.maxX; }
20644         }
20645
20646         if (this.constrainY) {
20647             if (y < this.minY) { y = this.minY; }
20648             if (y > this.maxY) { y = this.maxY; }
20649         }
20650
20651         x = this.getTick(x, this.xTicks);
20652         y = this.getTick(y, this.yTicks);
20653
20654
20655         return {x:x, y:y};
20656     },
20657
20658     /*
20659      * Sets up config options specific to this class. Overrides
20660      * Roo.dd.DragDrop, but all versions of this method through the
20661      * inheritance chain are called
20662      */
20663     applyConfig: function() {
20664         Roo.dd.DD.superclass.applyConfig.call(this);
20665         this.scroll = (this.config.scroll !== false);
20666     },
20667
20668     /*
20669      * Event that fires prior to the onMouseDown event.  Overrides
20670      * Roo.dd.DragDrop.
20671      */
20672     b4MouseDown: function(e) {
20673         // this.resetConstraints();
20674         this.autoOffset(e.getPageX(),
20675                             e.getPageY());
20676     },
20677
20678     /*
20679      * Event that fires prior to the onDrag event.  Overrides
20680      * Roo.dd.DragDrop.
20681      */
20682     b4Drag: function(e) {
20683         this.setDragElPos(e.getPageX(),
20684                             e.getPageY());
20685     },
20686
20687     toString: function() {
20688         return ("DD " + this.id);
20689     }
20690
20691     //////////////////////////////////////////////////////////////////////////
20692     // Debugging ygDragDrop events that can be overridden
20693     //////////////////////////////////////////////////////////////////////////
20694     /*
20695     startDrag: function(x, y) {
20696     },
20697
20698     onDrag: function(e) {
20699     },
20700
20701     onDragEnter: function(e, id) {
20702     },
20703
20704     onDragOver: function(e, id) {
20705     },
20706
20707     onDragOut: function(e, id) {
20708     },
20709
20710     onDragDrop: function(e, id) {
20711     },
20712
20713     endDrag: function(e) {
20714     }
20715
20716     */
20717
20718 });/*
20719  * Based on:
20720  * Ext JS Library 1.1.1
20721  * Copyright(c) 2006-2007, Ext JS, LLC.
20722  *
20723  * Originally Released Under LGPL - original licence link has changed is not relivant.
20724  *
20725  * Fork - LGPL
20726  * <script type="text/javascript">
20727  */
20728
20729 /**
20730  * @class Roo.dd.DDProxy
20731  * A DragDrop implementation that inserts an empty, bordered div into
20732  * the document that follows the cursor during drag operations.  At the time of
20733  * the click, the frame div is resized to the dimensions of the linked html
20734  * element, and moved to the exact location of the linked element.
20735  *
20736  * References to the "frame" element refer to the single proxy element that
20737  * was created to be dragged in place of all DDProxy elements on the
20738  * page.
20739  *
20740  * @extends Roo.dd.DD
20741  * @constructor
20742  * @param {String} id the id of the linked html element
20743  * @param {String} sGroup the group of related DragDrop objects
20744  * @param {object} config an object containing configurable attributes
20745  *                Valid properties for DDProxy in addition to those in DragDrop:
20746  *                   resizeFrame, centerFrame, dragElId
20747  */
20748 Roo.dd.DDProxy = function(id, sGroup, config) {
20749     if (id) {
20750         this.init(id, sGroup, config);
20751         this.initFrame();
20752     }
20753 };
20754
20755 /**
20756  * The default drag frame div id
20757  * @property Roo.dd.DDProxy.dragElId
20758  * @type String
20759  * @static
20760  */
20761 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20762
20763 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20764
20765     /**
20766      * By default we resize the drag frame to be the same size as the element
20767      * we want to drag (this is to get the frame effect).  We can turn it off
20768      * if we want a different behavior.
20769      * @property resizeFrame
20770      * @type boolean
20771      */
20772     resizeFrame: true,
20773
20774     /**
20775      * By default the frame is positioned exactly where the drag element is, so
20776      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20777      * you do not have constraints on the obj is to have the drag frame centered
20778      * around the cursor.  Set centerFrame to true for this effect.
20779      * @property centerFrame
20780      * @type boolean
20781      */
20782     centerFrame: false,
20783
20784     /**
20785      * Creates the proxy element if it does not yet exist
20786      * @method createFrame
20787      */
20788     createFrame: function() {
20789         var self = this;
20790         var body = document.body;
20791
20792         if (!body || !body.firstChild) {
20793             setTimeout( function() { self.createFrame(); }, 50 );
20794             return;
20795         }
20796
20797         var div = this.getDragEl();
20798
20799         if (!div) {
20800             div    = document.createElement("div");
20801             div.id = this.dragElId;
20802             var s  = div.style;
20803
20804             s.position   = "absolute";
20805             s.visibility = "hidden";
20806             s.cursor     = "move";
20807             s.border     = "2px solid #aaa";
20808             s.zIndex     = 999;
20809
20810             // appendChild can blow up IE if invoked prior to the window load event
20811             // while rendering a table.  It is possible there are other scenarios
20812             // that would cause this to happen as well.
20813             body.insertBefore(div, body.firstChild);
20814         }
20815     },
20816
20817     /**
20818      * Initialization for the drag frame element.  Must be called in the
20819      * constructor of all subclasses
20820      * @method initFrame
20821      */
20822     initFrame: function() {
20823         this.createFrame();
20824     },
20825
20826     applyConfig: function() {
20827         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20828
20829         this.resizeFrame = (this.config.resizeFrame !== false);
20830         this.centerFrame = (this.config.centerFrame);
20831         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20832     },
20833
20834     /**
20835      * Resizes the drag frame to the dimensions of the clicked object, positions
20836      * it over the object, and finally displays it
20837      * @method showFrame
20838      * @param {int} iPageX X click position
20839      * @param {int} iPageY Y click position
20840      * @private
20841      */
20842     showFrame: function(iPageX, iPageY) {
20843         var el = this.getEl();
20844         var dragEl = this.getDragEl();
20845         var s = dragEl.style;
20846
20847         this._resizeProxy();
20848
20849         if (this.centerFrame) {
20850             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20851                            Math.round(parseInt(s.height, 10)/2) );
20852         }
20853
20854         this.setDragElPos(iPageX, iPageY);
20855
20856         Roo.fly(dragEl).show();
20857     },
20858
20859     /**
20860      * The proxy is automatically resized to the dimensions of the linked
20861      * element when a drag is initiated, unless resizeFrame is set to false
20862      * @method _resizeProxy
20863      * @private
20864      */
20865     _resizeProxy: function() {
20866         if (this.resizeFrame) {
20867             var el = this.getEl();
20868             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20869         }
20870     },
20871
20872     // overrides Roo.dd.DragDrop
20873     b4MouseDown: function(e) {
20874         var x = e.getPageX();
20875         var y = e.getPageY();
20876         this.autoOffset(x, y);
20877         this.setDragElPos(x, y);
20878     },
20879
20880     // overrides Roo.dd.DragDrop
20881     b4StartDrag: function(x, y) {
20882         // show the drag frame
20883         this.showFrame(x, y);
20884     },
20885
20886     // overrides Roo.dd.DragDrop
20887     b4EndDrag: function(e) {
20888         Roo.fly(this.getDragEl()).hide();
20889     },
20890
20891     // overrides Roo.dd.DragDrop
20892     // By default we try to move the element to the last location of the frame.
20893     // This is so that the default behavior mirrors that of Roo.dd.DD.
20894     endDrag: function(e) {
20895
20896         var lel = this.getEl();
20897         var del = this.getDragEl();
20898
20899         // Show the drag frame briefly so we can get its position
20900         del.style.visibility = "";
20901
20902         this.beforeMove();
20903         // Hide the linked element before the move to get around a Safari
20904         // rendering bug.
20905         lel.style.visibility = "hidden";
20906         Roo.dd.DDM.moveToEl(lel, del);
20907         del.style.visibility = "hidden";
20908         lel.style.visibility = "";
20909
20910         this.afterDrag();
20911     },
20912
20913     beforeMove : function(){
20914
20915     },
20916
20917     afterDrag : function(){
20918
20919     },
20920
20921     toString: function() {
20922         return ("DDProxy " + this.id);
20923     }
20924
20925 });
20926 /*
20927  * Based on:
20928  * Ext JS Library 1.1.1
20929  * Copyright(c) 2006-2007, Ext JS, LLC.
20930  *
20931  * Originally Released Under LGPL - original licence link has changed is not relivant.
20932  *
20933  * Fork - LGPL
20934  * <script type="text/javascript">
20935  */
20936
20937  /**
20938  * @class Roo.dd.DDTarget
20939  * A DragDrop implementation that does not move, but can be a drop
20940  * target.  You would get the same result by simply omitting implementation
20941  * for the event callbacks, but this way we reduce the processing cost of the
20942  * event listener and the callbacks.
20943  * @extends Roo.dd.DragDrop
20944  * @constructor
20945  * @param {String} id the id of the element that is a drop target
20946  * @param {String} sGroup the group of related DragDrop objects
20947  * @param {object} config an object containing configurable attributes
20948  *                 Valid properties for DDTarget in addition to those in
20949  *                 DragDrop:
20950  *                    none
20951  */
20952 Roo.dd.DDTarget = function(id, sGroup, config) {
20953     if (id) {
20954         this.initTarget(id, sGroup, config);
20955     }
20956     if (config.listeners || config.events) { 
20957        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20958             listeners : config.listeners || {}, 
20959             events : config.events || {} 
20960         });    
20961     }
20962 };
20963
20964 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20965 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20966     toString: function() {
20967         return ("DDTarget " + this.id);
20968     }
20969 });
20970 /*
20971  * Based on:
20972  * Ext JS Library 1.1.1
20973  * Copyright(c) 2006-2007, Ext JS, LLC.
20974  *
20975  * Originally Released Under LGPL - original licence link has changed is not relivant.
20976  *
20977  * Fork - LGPL
20978  * <script type="text/javascript">
20979  */
20980  
20981
20982 /**
20983  * @class Roo.dd.ScrollManager
20984  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20985  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20986  * @singleton
20987  */
20988 Roo.dd.ScrollManager = function(){
20989     var ddm = Roo.dd.DragDropMgr;
20990     var els = {};
20991     var dragEl = null;
20992     var proc = {};
20993     
20994     
20995     
20996     var onStop = function(e){
20997         dragEl = null;
20998         clearProc();
20999     };
21000     
21001     var triggerRefresh = function(){
21002         if(ddm.dragCurrent){
21003              ddm.refreshCache(ddm.dragCurrent.groups);
21004         }
21005     };
21006     
21007     var doScroll = function(){
21008         if(ddm.dragCurrent){
21009             var dds = Roo.dd.ScrollManager;
21010             if(!dds.animate){
21011                 if(proc.el.scroll(proc.dir, dds.increment)){
21012                     triggerRefresh();
21013                 }
21014             }else{
21015                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21016             }
21017         }
21018     };
21019     
21020     var clearProc = function(){
21021         if(proc.id){
21022             clearInterval(proc.id);
21023         }
21024         proc.id = 0;
21025         proc.el = null;
21026         proc.dir = "";
21027     };
21028     
21029     var startProc = function(el, dir){
21030          Roo.log('scroll startproc');
21031         clearProc();
21032         proc.el = el;
21033         proc.dir = dir;
21034         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21035     };
21036     
21037     var onFire = function(e, isDrop){
21038        
21039         if(isDrop || !ddm.dragCurrent){ return; }
21040         var dds = Roo.dd.ScrollManager;
21041         if(!dragEl || dragEl != ddm.dragCurrent){
21042             dragEl = ddm.dragCurrent;
21043             // refresh regions on drag start
21044             dds.refreshCache();
21045         }
21046         
21047         var xy = Roo.lib.Event.getXY(e);
21048         var pt = new Roo.lib.Point(xy[0], xy[1]);
21049         for(var id in els){
21050             var el = els[id], r = el._region;
21051             if(r && r.contains(pt) && el.isScrollable()){
21052                 if(r.bottom - pt.y <= dds.thresh){
21053                     if(proc.el != el){
21054                         startProc(el, "down");
21055                     }
21056                     return;
21057                 }else if(r.right - pt.x <= dds.thresh){
21058                     if(proc.el != el){
21059                         startProc(el, "left");
21060                     }
21061                     return;
21062                 }else if(pt.y - r.top <= dds.thresh){
21063                     if(proc.el != el){
21064                         startProc(el, "up");
21065                     }
21066                     return;
21067                 }else if(pt.x - r.left <= dds.thresh){
21068                     if(proc.el != el){
21069                         startProc(el, "right");
21070                     }
21071                     return;
21072                 }
21073             }
21074         }
21075         clearProc();
21076     };
21077     
21078     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21079     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21080     
21081     return {
21082         /**
21083          * Registers new overflow element(s) to auto scroll
21084          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21085          */
21086         register : function(el){
21087             if(el instanceof Array){
21088                 for(var i = 0, len = el.length; i < len; i++) {
21089                         this.register(el[i]);
21090                 }
21091             }else{
21092                 el = Roo.get(el);
21093                 els[el.id] = el;
21094             }
21095             Roo.dd.ScrollManager.els = els;
21096         },
21097         
21098         /**
21099          * Unregisters overflow element(s) so they are no longer scrolled
21100          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21101          */
21102         unregister : function(el){
21103             if(el instanceof Array){
21104                 for(var i = 0, len = el.length; i < len; i++) {
21105                         this.unregister(el[i]);
21106                 }
21107             }else{
21108                 el = Roo.get(el);
21109                 delete els[el.id];
21110             }
21111         },
21112         
21113         /**
21114          * The number of pixels from the edge of a container the pointer needs to be to 
21115          * trigger scrolling (defaults to 25)
21116          * @type Number
21117          */
21118         thresh : 25,
21119         
21120         /**
21121          * The number of pixels to scroll in each scroll increment (defaults to 50)
21122          * @type Number
21123          */
21124         increment : 100,
21125         
21126         /**
21127          * The frequency of scrolls in milliseconds (defaults to 500)
21128          * @type Number
21129          */
21130         frequency : 500,
21131         
21132         /**
21133          * True to animate the scroll (defaults to true)
21134          * @type Boolean
21135          */
21136         animate: true,
21137         
21138         /**
21139          * The animation duration in seconds - 
21140          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21141          * @type Number
21142          */
21143         animDuration: .4,
21144         
21145         /**
21146          * Manually trigger a cache refresh.
21147          */
21148         refreshCache : function(){
21149             for(var id in els){
21150                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21151                     els[id]._region = els[id].getRegion();
21152                 }
21153             }
21154         }
21155     };
21156 }();/*
21157  * Based on:
21158  * Ext JS Library 1.1.1
21159  * Copyright(c) 2006-2007, Ext JS, LLC.
21160  *
21161  * Originally Released Under LGPL - original licence link has changed is not relivant.
21162  *
21163  * Fork - LGPL
21164  * <script type="text/javascript">
21165  */
21166  
21167
21168 /**
21169  * @class Roo.dd.Registry
21170  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21171  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21172  * @singleton
21173  */
21174 Roo.dd.Registry = function(){
21175     var elements = {}; 
21176     var handles = {}; 
21177     var autoIdSeed = 0;
21178
21179     var getId = function(el, autogen){
21180         if(typeof el == "string"){
21181             return el;
21182         }
21183         var id = el.id;
21184         if(!id && autogen !== false){
21185             id = "roodd-" + (++autoIdSeed);
21186             el.id = id;
21187         }
21188         return id;
21189     };
21190     
21191     return {
21192     /**
21193      * Register a drag drop element
21194      * @param {String|HTMLElement} element The id or DOM node to register
21195      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21196      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21197      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21198      * populated in the data object (if applicable):
21199      * <pre>
21200 Value      Description<br />
21201 ---------  ------------------------------------------<br />
21202 handles    Array of DOM nodes that trigger dragging<br />
21203            for the element being registered<br />
21204 isHandle   True if the element passed in triggers<br />
21205            dragging itself, else false
21206 </pre>
21207      */
21208         register : function(el, data){
21209             data = data || {};
21210             if(typeof el == "string"){
21211                 el = document.getElementById(el);
21212             }
21213             data.ddel = el;
21214             elements[getId(el)] = data;
21215             if(data.isHandle !== false){
21216                 handles[data.ddel.id] = data;
21217             }
21218             if(data.handles){
21219                 var hs = data.handles;
21220                 for(var i = 0, len = hs.length; i < len; i++){
21221                         handles[getId(hs[i])] = data;
21222                 }
21223             }
21224         },
21225
21226     /**
21227      * Unregister a drag drop element
21228      * @param {String|HTMLElement}  element The id or DOM node to unregister
21229      */
21230         unregister : function(el){
21231             var id = getId(el, false);
21232             var data = elements[id];
21233             if(data){
21234                 delete elements[id];
21235                 if(data.handles){
21236                     var hs = data.handles;
21237                     for(var i = 0, len = hs.length; i < len; i++){
21238                         delete handles[getId(hs[i], false)];
21239                     }
21240                 }
21241             }
21242         },
21243
21244     /**
21245      * Returns the handle registered for a DOM Node by id
21246      * @param {String|HTMLElement} id The DOM node or id to look up
21247      * @return {Object} handle The custom handle data
21248      */
21249         getHandle : function(id){
21250             if(typeof id != "string"){ // must be element?
21251                 id = id.id;
21252             }
21253             return handles[id];
21254         },
21255
21256     /**
21257      * Returns the handle that is registered for the DOM node that is the target of the event
21258      * @param {Event} e The event
21259      * @return {Object} handle The custom handle data
21260      */
21261         getHandleFromEvent : function(e){
21262             var t = Roo.lib.Event.getTarget(e);
21263             return t ? handles[t.id] : null;
21264         },
21265
21266     /**
21267      * Returns a custom data object that is registered for a DOM node by id
21268      * @param {String|HTMLElement} id The DOM node or id to look up
21269      * @return {Object} data The custom data
21270      */
21271         getTarget : function(id){
21272             if(typeof id != "string"){ // must be element?
21273                 id = id.id;
21274             }
21275             return elements[id];
21276         },
21277
21278     /**
21279      * Returns a custom data object that is registered for the DOM node that is the target of the event
21280      * @param {Event} e The event
21281      * @return {Object} data The custom data
21282      */
21283         getTargetFromEvent : function(e){
21284             var t = Roo.lib.Event.getTarget(e);
21285             return t ? elements[t.id] || handles[t.id] : null;
21286         }
21287     };
21288 }();/*
21289  * Based on:
21290  * Ext JS Library 1.1.1
21291  * Copyright(c) 2006-2007, Ext JS, LLC.
21292  *
21293  * Originally Released Under LGPL - original licence link has changed is not relivant.
21294  *
21295  * Fork - LGPL
21296  * <script type="text/javascript">
21297  */
21298  
21299
21300 /**
21301  * @class Roo.dd.StatusProxy
21302  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21303  * default drag proxy used by all Roo.dd components.
21304  * @constructor
21305  * @param {Object} config
21306  */
21307 Roo.dd.StatusProxy = function(config){
21308     Roo.apply(this, config);
21309     this.id = this.id || Roo.id();
21310     this.el = new Roo.Layer({
21311         dh: {
21312             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21313                 {tag: "div", cls: "x-dd-drop-icon"},
21314                 {tag: "div", cls: "x-dd-drag-ghost"}
21315             ]
21316         }, 
21317         shadow: !config || config.shadow !== false
21318     });
21319     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21320     this.dropStatus = this.dropNotAllowed;
21321 };
21322
21323 Roo.dd.StatusProxy.prototype = {
21324     /**
21325      * @cfg {String} dropAllowed
21326      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21327      */
21328     dropAllowed : "x-dd-drop-ok",
21329     /**
21330      * @cfg {String} dropNotAllowed
21331      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21332      */
21333     dropNotAllowed : "x-dd-drop-nodrop",
21334
21335     /**
21336      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21337      * over the current target element.
21338      * @param {String} cssClass The css class for the new drop status indicator image
21339      */
21340     setStatus : function(cssClass){
21341         cssClass = cssClass || this.dropNotAllowed;
21342         if(this.dropStatus != cssClass){
21343             this.el.replaceClass(this.dropStatus, cssClass);
21344             this.dropStatus = cssClass;
21345         }
21346     },
21347
21348     /**
21349      * Resets the status indicator to the default dropNotAllowed value
21350      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21351      */
21352     reset : function(clearGhost){
21353         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21354         this.dropStatus = this.dropNotAllowed;
21355         if(clearGhost){
21356             this.ghost.update("");
21357         }
21358     },
21359
21360     /**
21361      * Updates the contents of the ghost element
21362      * @param {String} html The html that will replace the current innerHTML of the ghost element
21363      */
21364     update : function(html){
21365         if(typeof html == "string"){
21366             this.ghost.update(html);
21367         }else{
21368             this.ghost.update("");
21369             html.style.margin = "0";
21370             this.ghost.dom.appendChild(html);
21371         }
21372         // ensure float = none set?? cant remember why though.
21373         var el = this.ghost.dom.firstChild;
21374                 if(el){
21375                         Roo.fly(el).setStyle('float', 'none');
21376                 }
21377     },
21378     
21379     /**
21380      * Returns the underlying proxy {@link Roo.Layer}
21381      * @return {Roo.Layer} el
21382     */
21383     getEl : function(){
21384         return this.el;
21385     },
21386
21387     /**
21388      * Returns the ghost element
21389      * @return {Roo.Element} el
21390      */
21391     getGhost : function(){
21392         return this.ghost;
21393     },
21394
21395     /**
21396      * Hides the proxy
21397      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21398      */
21399     hide : function(clear){
21400         this.el.hide();
21401         if(clear){
21402             this.reset(true);
21403         }
21404     },
21405
21406     /**
21407      * Stops the repair animation if it's currently running
21408      */
21409     stop : function(){
21410         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21411             this.anim.stop();
21412         }
21413     },
21414
21415     /**
21416      * Displays this proxy
21417      */
21418     show : function(){
21419         this.el.show();
21420     },
21421
21422     /**
21423      * Force the Layer to sync its shadow and shim positions to the element
21424      */
21425     sync : function(){
21426         this.el.sync();
21427     },
21428
21429     /**
21430      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21431      * invalid drop operation by the item being dragged.
21432      * @param {Array} xy The XY position of the element ([x, y])
21433      * @param {Function} callback The function to call after the repair is complete
21434      * @param {Object} scope The scope in which to execute the callback
21435      */
21436     repair : function(xy, callback, scope){
21437         this.callback = callback;
21438         this.scope = scope;
21439         if(xy && this.animRepair !== false){
21440             this.el.addClass("x-dd-drag-repair");
21441             this.el.hideUnders(true);
21442             this.anim = this.el.shift({
21443                 duration: this.repairDuration || .5,
21444                 easing: 'easeOut',
21445                 xy: xy,
21446                 stopFx: true,
21447                 callback: this.afterRepair,
21448                 scope: this
21449             });
21450         }else{
21451             this.afterRepair();
21452         }
21453     },
21454
21455     // private
21456     afterRepair : function(){
21457         this.hide(true);
21458         if(typeof this.callback == "function"){
21459             this.callback.call(this.scope || this);
21460         }
21461         this.callback = null;
21462         this.scope = null;
21463     }
21464 };/*
21465  * Based on:
21466  * Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  *
21469  * Originally Released Under LGPL - original licence link has changed is not relivant.
21470  *
21471  * Fork - LGPL
21472  * <script type="text/javascript">
21473  */
21474
21475 /**
21476  * @class Roo.dd.DragSource
21477  * @extends Roo.dd.DDProxy
21478  * A simple class that provides the basic implementation needed to make any element draggable.
21479  * @constructor
21480  * @param {String/HTMLElement/Element} el The container element
21481  * @param {Object} config
21482  */
21483 Roo.dd.DragSource = function(el, config){
21484     this.el = Roo.get(el);
21485     this.dragData = {};
21486     
21487     Roo.apply(this, config);
21488     
21489     if(!this.proxy){
21490         this.proxy = new Roo.dd.StatusProxy();
21491     }
21492
21493     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21494           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21495     
21496     this.dragging = false;
21497 };
21498
21499 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21500     /**
21501      * @cfg {String} dropAllowed
21502      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21503      */
21504     dropAllowed : "x-dd-drop-ok",
21505     /**
21506      * @cfg {String} dropNotAllowed
21507      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21508      */
21509     dropNotAllowed : "x-dd-drop-nodrop",
21510
21511     /**
21512      * Returns the data object associated with this drag source
21513      * @return {Object} data An object containing arbitrary data
21514      */
21515     getDragData : function(e){
21516         return this.dragData;
21517     },
21518
21519     // private
21520     onDragEnter : function(e, id){
21521         var target = Roo.dd.DragDropMgr.getDDById(id);
21522         this.cachedTarget = target;
21523         if(this.beforeDragEnter(target, e, id) !== false){
21524             if(target.isNotifyTarget){
21525                 var status = target.notifyEnter(this, e, this.dragData);
21526                 this.proxy.setStatus(status);
21527             }else{
21528                 this.proxy.setStatus(this.dropAllowed);
21529             }
21530             
21531             if(this.afterDragEnter){
21532                 /**
21533                  * An empty function by default, but provided so that you can perform a custom action
21534                  * when the dragged item enters the drop target by providing an implementation.
21535                  * @param {Roo.dd.DragDrop} target The drop target
21536                  * @param {Event} e The event object
21537                  * @param {String} id The id of the dragged element
21538                  * @method afterDragEnter
21539                  */
21540                 this.afterDragEnter(target, e, id);
21541             }
21542         }
21543     },
21544
21545     /**
21546      * An empty function by default, but provided so that you can perform a custom action
21547      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21548      * @param {Roo.dd.DragDrop} target The drop target
21549      * @param {Event} e The event object
21550      * @param {String} id The id of the dragged element
21551      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21552      */
21553     beforeDragEnter : function(target, e, id){
21554         return true;
21555     },
21556
21557     // private
21558     alignElWithMouse: function() {
21559         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21560         this.proxy.sync();
21561     },
21562
21563     // private
21564     onDragOver : function(e, id){
21565         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21566         if(this.beforeDragOver(target, e, id) !== false){
21567             if(target.isNotifyTarget){
21568                 var status = target.notifyOver(this, e, this.dragData);
21569                 this.proxy.setStatus(status);
21570             }
21571
21572             if(this.afterDragOver){
21573                 /**
21574                  * An empty function by default, but provided so that you can perform a custom action
21575                  * while the dragged item is over the drop target by providing an implementation.
21576                  * @param {Roo.dd.DragDrop} target The drop target
21577                  * @param {Event} e The event object
21578                  * @param {String} id The id of the dragged element
21579                  * @method afterDragOver
21580                  */
21581                 this.afterDragOver(target, e, id);
21582             }
21583         }
21584     },
21585
21586     /**
21587      * An empty function by default, but provided so that you can perform a custom action
21588      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21589      * @param {Roo.dd.DragDrop} target The drop target
21590      * @param {Event} e The event object
21591      * @param {String} id The id of the dragged element
21592      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21593      */
21594     beforeDragOver : function(target, e, id){
21595         return true;
21596     },
21597
21598     // private
21599     onDragOut : function(e, id){
21600         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21601         if(this.beforeDragOut(target, e, id) !== false){
21602             if(target.isNotifyTarget){
21603                 target.notifyOut(this, e, this.dragData);
21604             }
21605             this.proxy.reset();
21606             if(this.afterDragOut){
21607                 /**
21608                  * An empty function by default, but provided so that you can perform a custom action
21609                  * after the dragged item is dragged out of the target without dropping.
21610                  * @param {Roo.dd.DragDrop} target The drop target
21611                  * @param {Event} e The event object
21612                  * @param {String} id The id of the dragged element
21613                  * @method afterDragOut
21614                  */
21615                 this.afterDragOut(target, e, id);
21616             }
21617         }
21618         this.cachedTarget = null;
21619     },
21620
21621     /**
21622      * An empty function by default, but provided so that you can perform a custom action before the dragged
21623      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21624      * @param {Roo.dd.DragDrop} target The drop target
21625      * @param {Event} e The event object
21626      * @param {String} id The id of the dragged element
21627      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21628      */
21629     beforeDragOut : function(target, e, id){
21630         return true;
21631     },
21632     
21633     // private
21634     onDragDrop : function(e, id){
21635         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21636         if(this.beforeDragDrop(target, e, id) !== false){
21637             if(target.isNotifyTarget){
21638                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21639                     this.onValidDrop(target, e, id);
21640                 }else{
21641                     this.onInvalidDrop(target, e, id);
21642                 }
21643             }else{
21644                 this.onValidDrop(target, e, id);
21645             }
21646             
21647             if(this.afterDragDrop){
21648                 /**
21649                  * An empty function by default, but provided so that you can perform a custom action
21650                  * after a valid drag drop has occurred by providing an implementation.
21651                  * @param {Roo.dd.DragDrop} target The drop target
21652                  * @param {Event} e The event object
21653                  * @param {String} id The id of the dropped element
21654                  * @method afterDragDrop
21655                  */
21656                 this.afterDragDrop(target, e, id);
21657             }
21658         }
21659         delete this.cachedTarget;
21660     },
21661
21662     /**
21663      * An empty function by default, but provided so that you can perform a custom action before the dragged
21664      * item is dropped onto the target and optionally cancel the onDragDrop.
21665      * @param {Roo.dd.DragDrop} target The drop target
21666      * @param {Event} e The event object
21667      * @param {String} id The id of the dragged element
21668      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21669      */
21670     beforeDragDrop : function(target, e, id){
21671         return true;
21672     },
21673
21674     // private
21675     onValidDrop : function(target, e, id){
21676         this.hideProxy();
21677         if(this.afterValidDrop){
21678             /**
21679              * An empty function by default, but provided so that you can perform a custom action
21680              * after a valid drop has occurred by providing an implementation.
21681              * @param {Object} target The target DD 
21682              * @param {Event} e The event object
21683              * @param {String} id The id of the dropped element
21684              * @method afterInvalidDrop
21685              */
21686             this.afterValidDrop(target, e, id);
21687         }
21688     },
21689
21690     // private
21691     getRepairXY : function(e, data){
21692         return this.el.getXY();  
21693     },
21694
21695     // private
21696     onInvalidDrop : function(target, e, id){
21697         this.beforeInvalidDrop(target, e, id);
21698         if(this.cachedTarget){
21699             if(this.cachedTarget.isNotifyTarget){
21700                 this.cachedTarget.notifyOut(this, e, this.dragData);
21701             }
21702             this.cacheTarget = null;
21703         }
21704         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21705
21706         if(this.afterInvalidDrop){
21707             /**
21708              * An empty function by default, but provided so that you can perform a custom action
21709              * after an invalid drop has occurred by providing an implementation.
21710              * @param {Event} e The event object
21711              * @param {String} id The id of the dropped element
21712              * @method afterInvalidDrop
21713              */
21714             this.afterInvalidDrop(e, id);
21715         }
21716     },
21717
21718     // private
21719     afterRepair : function(){
21720         if(Roo.enableFx){
21721             this.el.highlight(this.hlColor || "c3daf9");
21722         }
21723         this.dragging = false;
21724     },
21725
21726     /**
21727      * An empty function by default, but provided so that you can perform a custom action after an invalid
21728      * drop has occurred.
21729      * @param {Roo.dd.DragDrop} target The drop target
21730      * @param {Event} e The event object
21731      * @param {String} id The id of the dragged element
21732      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21733      */
21734     beforeInvalidDrop : function(target, e, id){
21735         return true;
21736     },
21737
21738     // private
21739     handleMouseDown : function(e){
21740         if(this.dragging) {
21741             return;
21742         }
21743         var data = this.getDragData(e);
21744         if(data && this.onBeforeDrag(data, e) !== false){
21745             this.dragData = data;
21746             this.proxy.stop();
21747             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21748         } 
21749     },
21750
21751     /**
21752      * An empty function by default, but provided so that you can perform a custom action before the initial
21753      * drag event begins and optionally cancel it.
21754      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21755      * @param {Event} e The event object
21756      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21757      */
21758     onBeforeDrag : function(data, e){
21759         return true;
21760     },
21761
21762     /**
21763      * An empty function by default, but provided so that you can perform a custom action once the initial
21764      * drag event has begun.  The drag cannot be canceled from this function.
21765      * @param {Number} x The x position of the click on the dragged object
21766      * @param {Number} y The y position of the click on the dragged object
21767      */
21768     onStartDrag : Roo.emptyFn,
21769
21770     // private - YUI override
21771     startDrag : function(x, y){
21772         this.proxy.reset();
21773         this.dragging = true;
21774         this.proxy.update("");
21775         this.onInitDrag(x, y);
21776         this.proxy.show();
21777     },
21778
21779     // private
21780     onInitDrag : function(x, y){
21781         var clone = this.el.dom.cloneNode(true);
21782         clone.id = Roo.id(); // prevent duplicate ids
21783         this.proxy.update(clone);
21784         this.onStartDrag(x, y);
21785         return true;
21786     },
21787
21788     /**
21789      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21790      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21791      */
21792     getProxy : function(){
21793         return this.proxy;  
21794     },
21795
21796     /**
21797      * Hides the drag source's {@link Roo.dd.StatusProxy}
21798      */
21799     hideProxy : function(){
21800         this.proxy.hide();  
21801         this.proxy.reset(true);
21802         this.dragging = false;
21803     },
21804
21805     // private
21806     triggerCacheRefresh : function(){
21807         Roo.dd.DDM.refreshCache(this.groups);
21808     },
21809
21810     // private - override to prevent hiding
21811     b4EndDrag: function(e) {
21812     },
21813
21814     // private - override to prevent moving
21815     endDrag : function(e){
21816         this.onEndDrag(this.dragData, e);
21817     },
21818
21819     // private
21820     onEndDrag : function(data, e){
21821     },
21822     
21823     // private - pin to cursor
21824     autoOffset : function(x, y) {
21825         this.setDelta(-12, -20);
21826     }    
21827 });/*
21828  * Based on:
21829  * Ext JS Library 1.1.1
21830  * Copyright(c) 2006-2007, Ext JS, LLC.
21831  *
21832  * Originally Released Under LGPL - original licence link has changed is not relivant.
21833  *
21834  * Fork - LGPL
21835  * <script type="text/javascript">
21836  */
21837
21838
21839 /**
21840  * @class Roo.dd.DropTarget
21841  * @extends Roo.dd.DDTarget
21842  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21843  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21844  * @constructor
21845  * @param {String/HTMLElement/Element} el The container element
21846  * @param {Object} config
21847  */
21848 Roo.dd.DropTarget = function(el, config){
21849     this.el = Roo.get(el);
21850     
21851     var listeners = false; ;
21852     if (config && config.listeners) {
21853         listeners= config.listeners;
21854         delete config.listeners;
21855     }
21856     Roo.apply(this, config);
21857     
21858     if(this.containerScroll){
21859         Roo.dd.ScrollManager.register(this.el);
21860     }
21861     this.addEvents( {
21862          /**
21863          * @scope Roo.dd.DropTarget
21864          */
21865          
21866          /**
21867          * @event enter
21868          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21869          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21870          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21871          * 
21872          * IMPORTANT : it should set this.overClass and this.dropAllowed
21873          * 
21874          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21875          * @param {Event} e The event
21876          * @param {Object} data An object containing arbitrary data supplied by the drag source
21877          */
21878         "enter" : true,
21879         
21880          /**
21881          * @event over
21882          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21883          * This method will be called on every mouse movement while the drag source is over the drop target.
21884          * This default implementation simply returns the dropAllowed config value.
21885          * 
21886          * IMPORTANT : it should set this.dropAllowed
21887          * 
21888          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21889          * @param {Event} e The event
21890          * @param {Object} data An object containing arbitrary data supplied by the drag source
21891          
21892          */
21893         "over" : true,
21894         /**
21895          * @event out
21896          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21897          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21898          * overClass (if any) from the drop element.
21899          * 
21900          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21901          * @param {Event} e The event
21902          * @param {Object} data An object containing arbitrary data supplied by the drag source
21903          */
21904          "out" : true,
21905          
21906         /**
21907          * @event drop
21908          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21909          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21910          * implementation that does something to process the drop event and returns true so that the drag source's
21911          * repair action does not run.
21912          * 
21913          * IMPORTANT : it should set this.success
21914          * 
21915          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21916          * @param {Event} e The event
21917          * @param {Object} data An object containing arbitrary data supplied by the drag source
21918         */
21919          "drop" : true
21920     });
21921             
21922      
21923     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21924         this.el.dom, 
21925         this.ddGroup || this.group,
21926         {
21927             isTarget: true,
21928             listeners : listeners || {} 
21929            
21930         
21931         }
21932     );
21933
21934 };
21935
21936 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21937     /**
21938      * @cfg {String} overClass
21939      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21940      */
21941      /**
21942      * @cfg {String} ddGroup
21943      * The drag drop group to handle drop events for
21944      */
21945      
21946     /**
21947      * @cfg {String} dropAllowed
21948      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21949      */
21950     dropAllowed : "x-dd-drop-ok",
21951     /**
21952      * @cfg {String} dropNotAllowed
21953      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21954      */
21955     dropNotAllowed : "x-dd-drop-nodrop",
21956     /**
21957      * @cfg {boolean} success
21958      * set this after drop listener.. 
21959      */
21960     success : false,
21961     /**
21962      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21963      * if the drop point is valid for over/enter..
21964      */
21965     valid : false,
21966     // private
21967     isTarget : true,
21968
21969     // private
21970     isNotifyTarget : true,
21971     
21972     /**
21973      * @hide
21974      */
21975     notifyEnter : function(dd, e, data)
21976     {
21977         this.valid = true;
21978         this.fireEvent('enter', dd, e, data);
21979         if(this.overClass){
21980             this.el.addClass(this.overClass);
21981         }
21982         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21983             this.valid ? this.dropAllowed : this.dropNotAllowed
21984         );
21985     },
21986
21987     /**
21988      * @hide
21989      */
21990     notifyOver : function(dd, e, data)
21991     {
21992         this.valid = true;
21993         this.fireEvent('over', dd, e, data);
21994         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21995             this.valid ? this.dropAllowed : this.dropNotAllowed
21996         );
21997     },
21998
21999     /**
22000      * @hide
22001      */
22002     notifyOut : function(dd, e, data)
22003     {
22004         this.fireEvent('out', dd, e, data);
22005         if(this.overClass){
22006             this.el.removeClass(this.overClass);
22007         }
22008     },
22009
22010     /**
22011      * @hide
22012      */
22013     notifyDrop : function(dd, e, data)
22014     {
22015         this.success = false;
22016         this.fireEvent('drop', dd, e, data);
22017         return this.success;
22018     }
22019 });/*
22020  * Based on:
22021  * Ext JS Library 1.1.1
22022  * Copyright(c) 2006-2007, Ext JS, LLC.
22023  *
22024  * Originally Released Under LGPL - original licence link has changed is not relivant.
22025  *
22026  * Fork - LGPL
22027  * <script type="text/javascript">
22028  */
22029
22030
22031 /**
22032  * @class Roo.dd.DragZone
22033  * @extends Roo.dd.DragSource
22034  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22035  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22036  * @constructor
22037  * @param {String/HTMLElement/Element} el The container element
22038  * @param {Object} config
22039  */
22040 Roo.dd.DragZone = function(el, config){
22041     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22042     if(this.containerScroll){
22043         Roo.dd.ScrollManager.register(this.el);
22044     }
22045 };
22046
22047 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22048     /**
22049      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22050      * for auto scrolling during drag operations.
22051      */
22052     /**
22053      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22054      * method after a failed drop (defaults to "c3daf9" - light blue)
22055      */
22056
22057     /**
22058      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22059      * for a valid target to drag based on the mouse down. Override this method
22060      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22061      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22062      * @param {EventObject} e The mouse down event
22063      * @return {Object} The dragData
22064      */
22065     getDragData : function(e){
22066         return Roo.dd.Registry.getHandleFromEvent(e);
22067     },
22068     
22069     /**
22070      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22071      * this.dragData.ddel
22072      * @param {Number} x The x position of the click on the dragged object
22073      * @param {Number} y The y position of the click on the dragged object
22074      * @return {Boolean} true to continue the drag, false to cancel
22075      */
22076     onInitDrag : function(x, y){
22077         this.proxy.update(this.dragData.ddel.cloneNode(true));
22078         this.onStartDrag(x, y);
22079         return true;
22080     },
22081     
22082     /**
22083      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22084      */
22085     afterRepair : function(){
22086         if(Roo.enableFx){
22087             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22088         }
22089         this.dragging = false;
22090     },
22091
22092     /**
22093      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22094      * the XY of this.dragData.ddel
22095      * @param {EventObject} e The mouse up event
22096      * @return {Array} The xy location (e.g. [100, 200])
22097      */
22098     getRepairXY : function(e){
22099         return Roo.Element.fly(this.dragData.ddel).getXY();  
22100     }
22101 });/*
22102  * Based on:
22103  * Ext JS Library 1.1.1
22104  * Copyright(c) 2006-2007, Ext JS, LLC.
22105  *
22106  * Originally Released Under LGPL - original licence link has changed is not relivant.
22107  *
22108  * Fork - LGPL
22109  * <script type="text/javascript">
22110  */
22111 /**
22112  * @class Roo.dd.DropZone
22113  * @extends Roo.dd.DropTarget
22114  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22115  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22116  * @constructor
22117  * @param {String/HTMLElement/Element} el The container element
22118  * @param {Object} config
22119  */
22120 Roo.dd.DropZone = function(el, config){
22121     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22122 };
22123
22124 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22125     /**
22126      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22127      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22128      * provide your own custom lookup.
22129      * @param {Event} e The event
22130      * @return {Object} data The custom data
22131      */
22132     getTargetFromEvent : function(e){
22133         return Roo.dd.Registry.getTargetFromEvent(e);
22134     },
22135
22136     /**
22137      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22138      * that it has registered.  This method has no default implementation and should be overridden to provide
22139      * node-specific processing if necessary.
22140      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22141      * {@link #getTargetFromEvent} for this node)
22142      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22143      * @param {Event} e The event
22144      * @param {Object} data An object containing arbitrary data supplied by the drag source
22145      */
22146     onNodeEnter : function(n, dd, e, data){
22147         
22148     },
22149
22150     /**
22151      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22152      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22153      * overridden to provide the proper feedback.
22154      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22155      * {@link #getTargetFromEvent} for this node)
22156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22157      * @param {Event} e The event
22158      * @param {Object} data An object containing arbitrary data supplied by the drag source
22159      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22160      * underlying {@link Roo.dd.StatusProxy} can be updated
22161      */
22162     onNodeOver : function(n, dd, e, data){
22163         return this.dropAllowed;
22164     },
22165
22166     /**
22167      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22168      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22169      * node-specific processing if necessary.
22170      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22171      * {@link #getTargetFromEvent} for this node)
22172      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22173      * @param {Event} e The event
22174      * @param {Object} data An object containing arbitrary data supplied by the drag source
22175      */
22176     onNodeOut : function(n, dd, e, data){
22177         
22178     },
22179
22180     /**
22181      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22182      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22183      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22184      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22185      * {@link #getTargetFromEvent} for this node)
22186      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22187      * @param {Event} e The event
22188      * @param {Object} data An object containing arbitrary data supplied by the drag source
22189      * @return {Boolean} True if the drop was valid, else false
22190      */
22191     onNodeDrop : function(n, dd, e, data){
22192         return false;
22193     },
22194
22195     /**
22196      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22197      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22198      * it should be overridden to provide the proper feedback if necessary.
22199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22200      * @param {Event} e The event
22201      * @param {Object} data An object containing arbitrary data supplied by the drag source
22202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22203      * underlying {@link Roo.dd.StatusProxy} can be updated
22204      */
22205     onContainerOver : function(dd, e, data){
22206         return this.dropNotAllowed;
22207     },
22208
22209     /**
22210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22211      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22212      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22213      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22214      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22215      * @param {Event} e The event
22216      * @param {Object} data An object containing arbitrary data supplied by the drag source
22217      * @return {Boolean} True if the drop was valid, else false
22218      */
22219     onContainerDrop : function(dd, e, data){
22220         return false;
22221     },
22222
22223     /**
22224      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22225      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22226      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22227      * you should override this method and provide a custom implementation.
22228      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22229      * @param {Event} e The event
22230      * @param {Object} data An object containing arbitrary data supplied by the drag source
22231      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22232      * underlying {@link Roo.dd.StatusProxy} can be updated
22233      */
22234     notifyEnter : function(dd, e, data){
22235         return this.dropNotAllowed;
22236     },
22237
22238     /**
22239      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22240      * This method will be called on every mouse movement while the drag source is over the drop zone.
22241      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22242      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22243      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22244      * registered node, it will call {@link #onContainerOver}.
22245      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22246      * @param {Event} e The event
22247      * @param {Object} data An object containing arbitrary data supplied by the drag source
22248      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22249      * underlying {@link Roo.dd.StatusProxy} can be updated
22250      */
22251     notifyOver : function(dd, e, data){
22252         var n = this.getTargetFromEvent(e);
22253         if(!n){ // not over valid drop target
22254             if(this.lastOverNode){
22255                 this.onNodeOut(this.lastOverNode, dd, e, data);
22256                 this.lastOverNode = null;
22257             }
22258             return this.onContainerOver(dd, e, data);
22259         }
22260         if(this.lastOverNode != n){
22261             if(this.lastOverNode){
22262                 this.onNodeOut(this.lastOverNode, dd, e, data);
22263             }
22264             this.onNodeEnter(n, dd, e, data);
22265             this.lastOverNode = n;
22266         }
22267         return this.onNodeOver(n, dd, e, data);
22268     },
22269
22270     /**
22271      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22272      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22273      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22275      * @param {Event} e The event
22276      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22277      */
22278     notifyOut : function(dd, e, data){
22279         if(this.lastOverNode){
22280             this.onNodeOut(this.lastOverNode, dd, e, data);
22281             this.lastOverNode = null;
22282         }
22283     },
22284
22285     /**
22286      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22287      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22288      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22289      * otherwise it will call {@link #onContainerDrop}.
22290      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22291      * @param {Event} e The event
22292      * @param {Object} data An object containing arbitrary data supplied by the drag source
22293      * @return {Boolean} True if the drop was valid, else false
22294      */
22295     notifyDrop : function(dd, e, data){
22296         if(this.lastOverNode){
22297             this.onNodeOut(this.lastOverNode, dd, e, data);
22298             this.lastOverNode = null;
22299         }
22300         var n = this.getTargetFromEvent(e);
22301         return n ?
22302             this.onNodeDrop(n, dd, e, data) :
22303             this.onContainerDrop(dd, e, data);
22304     },
22305
22306     // private
22307     triggerCacheRefresh : function(){
22308         Roo.dd.DDM.refreshCache(this.groups);
22309     }  
22310 });/*
22311  * Based on:
22312  * Ext JS Library 1.1.1
22313  * Copyright(c) 2006-2007, Ext JS, LLC.
22314  *
22315  * Originally Released Under LGPL - original licence link has changed is not relivant.
22316  *
22317  * Fork - LGPL
22318  * <script type="text/javascript">
22319  */
22320
22321
22322 /**
22323  * @class Roo.data.SortTypes
22324  * @singleton
22325  * Defines the default sorting (casting?) comparison functions used when sorting data.
22326  */
22327 Roo.data.SortTypes = {
22328     /**
22329      * Default sort that does nothing
22330      * @param {Mixed} s The value being converted
22331      * @return {Mixed} The comparison value
22332      */
22333     none : function(s){
22334         return s;
22335     },
22336     
22337     /**
22338      * The regular expression used to strip tags
22339      * @type {RegExp}
22340      * @property
22341      */
22342     stripTagsRE : /<\/?[^>]+>/gi,
22343     
22344     /**
22345      * Strips all HTML tags to sort on text only
22346      * @param {Mixed} s The value being converted
22347      * @return {String} The comparison value
22348      */
22349     asText : function(s){
22350         return String(s).replace(this.stripTagsRE, "");
22351     },
22352     
22353     /**
22354      * Strips all HTML tags to sort on text only - Case insensitive
22355      * @param {Mixed} s The value being converted
22356      * @return {String} The comparison value
22357      */
22358     asUCText : function(s){
22359         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22360     },
22361     
22362     /**
22363      * Case insensitive string
22364      * @param {Mixed} s The value being converted
22365      * @return {String} The comparison value
22366      */
22367     asUCString : function(s) {
22368         return String(s).toUpperCase();
22369     },
22370     
22371     /**
22372      * Date sorting
22373      * @param {Mixed} s The value being converted
22374      * @return {Number} The comparison value
22375      */
22376     asDate : function(s) {
22377         if(!s){
22378             return 0;
22379         }
22380         if(s instanceof Date){
22381             return s.getTime();
22382         }
22383         return Date.parse(String(s));
22384     },
22385     
22386     /**
22387      * Float sorting
22388      * @param {Mixed} s The value being converted
22389      * @return {Float} The comparison value
22390      */
22391     asFloat : function(s) {
22392         var val = parseFloat(String(s).replace(/,/g, ""));
22393         if(isNaN(val)) {
22394             val = 0;
22395         }
22396         return val;
22397     },
22398     
22399     /**
22400      * Integer sorting
22401      * @param {Mixed} s The value being converted
22402      * @return {Number} The comparison value
22403      */
22404     asInt : function(s) {
22405         var val = parseInt(String(s).replace(/,/g, ""));
22406         if(isNaN(val)) {
22407             val = 0;
22408         }
22409         return val;
22410     }
22411 };/*
22412  * Based on:
22413  * Ext JS Library 1.1.1
22414  * Copyright(c) 2006-2007, Ext JS, LLC.
22415  *
22416  * Originally Released Under LGPL - original licence link has changed is not relivant.
22417  *
22418  * Fork - LGPL
22419  * <script type="text/javascript">
22420  */
22421
22422 /**
22423 * @class Roo.data.Record
22424  * Instances of this class encapsulate both record <em>definition</em> information, and record
22425  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22426  * to access Records cached in an {@link Roo.data.Store} object.<br>
22427  * <p>
22428  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22429  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22430  * objects.<br>
22431  * <p>
22432  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22433  * @constructor
22434  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22435  * {@link #create}. The parameters are the same.
22436  * @param {Array} data An associative Array of data values keyed by the field name.
22437  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22438  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22439  * not specified an integer id is generated.
22440  */
22441 Roo.data.Record = function(data, id){
22442     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22443     this.data = data;
22444 };
22445
22446 /**
22447  * Generate a constructor for a specific record layout.
22448  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22449  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22450  * Each field definition object may contain the following properties: <ul>
22451  * <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,
22452  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22453  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22454  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22455  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22456  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22457  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22458  * this may be omitted.</p></li>
22459  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22460  * <ul><li>auto (Default, implies no conversion)</li>
22461  * <li>string</li>
22462  * <li>int</li>
22463  * <li>float</li>
22464  * <li>boolean</li>
22465  * <li>date</li></ul></p></li>
22466  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22467  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22468  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22469  * by the Reader into an object that will be stored in the Record. It is passed the
22470  * following parameters:<ul>
22471  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22472  * </ul></p></li>
22473  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22474  * </ul>
22475  * <br>usage:<br><pre><code>
22476 var TopicRecord = Roo.data.Record.create(
22477     {name: 'title', mapping: 'topic_title'},
22478     {name: 'author', mapping: 'username'},
22479     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22480     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22481     {name: 'lastPoster', mapping: 'user2'},
22482     {name: 'excerpt', mapping: 'post_text'}
22483 );
22484
22485 var myNewRecord = new TopicRecord({
22486     title: 'Do my job please',
22487     author: 'noobie',
22488     totalPosts: 1,
22489     lastPost: new Date(),
22490     lastPoster: 'Animal',
22491     excerpt: 'No way dude!'
22492 });
22493 myStore.add(myNewRecord);
22494 </code></pre>
22495  * @method create
22496  * @static
22497  */
22498 Roo.data.Record.create = function(o){
22499     var f = function(){
22500         f.superclass.constructor.apply(this, arguments);
22501     };
22502     Roo.extend(f, Roo.data.Record);
22503     var p = f.prototype;
22504     p.fields = new Roo.util.MixedCollection(false, function(field){
22505         return field.name;
22506     });
22507     for(var i = 0, len = o.length; i < len; i++){
22508         p.fields.add(new Roo.data.Field(o[i]));
22509     }
22510     f.getField = function(name){
22511         return p.fields.get(name);  
22512     };
22513     return f;
22514 };
22515
22516 Roo.data.Record.AUTO_ID = 1000;
22517 Roo.data.Record.EDIT = 'edit';
22518 Roo.data.Record.REJECT = 'reject';
22519 Roo.data.Record.COMMIT = 'commit';
22520
22521 Roo.data.Record.prototype = {
22522     /**
22523      * Readonly flag - true if this record has been modified.
22524      * @type Boolean
22525      */
22526     dirty : false,
22527     editing : false,
22528     error: null,
22529     modified: null,
22530
22531     // private
22532     join : function(store){
22533         this.store = store;
22534     },
22535
22536     /**
22537      * Set the named field to the specified value.
22538      * @param {String} name The name of the field to set.
22539      * @param {Object} value The value to set the field to.
22540      */
22541     set : function(name, value){
22542         if(this.data[name] == value){
22543             return;
22544         }
22545         this.dirty = true;
22546         if(!this.modified){
22547             this.modified = {};
22548         }
22549         if(typeof this.modified[name] == 'undefined'){
22550             this.modified[name] = this.data[name];
22551         }
22552         this.data[name] = value;
22553         if(!this.editing && this.store){
22554             this.store.afterEdit(this);
22555         }       
22556     },
22557
22558     /**
22559      * Get the value of the named field.
22560      * @param {String} name The name of the field to get the value of.
22561      * @return {Object} The value of the field.
22562      */
22563     get : function(name){
22564         return this.data[name]; 
22565     },
22566
22567     // private
22568     beginEdit : function(){
22569         this.editing = true;
22570         this.modified = {}; 
22571     },
22572
22573     // private
22574     cancelEdit : function(){
22575         this.editing = false;
22576         delete this.modified;
22577     },
22578
22579     // private
22580     endEdit : function(){
22581         this.editing = false;
22582         if(this.dirty && this.store){
22583             this.store.afterEdit(this);
22584         }
22585     },
22586
22587     /**
22588      * Usually called by the {@link Roo.data.Store} which owns the Record.
22589      * Rejects all changes made to the Record since either creation, or the last commit operation.
22590      * Modified fields are reverted to their original values.
22591      * <p>
22592      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22593      * of reject operations.
22594      */
22595     reject : function(){
22596         var m = this.modified;
22597         for(var n in m){
22598             if(typeof m[n] != "function"){
22599                 this.data[n] = m[n];
22600             }
22601         }
22602         this.dirty = false;
22603         delete this.modified;
22604         this.editing = false;
22605         if(this.store){
22606             this.store.afterReject(this);
22607         }
22608     },
22609
22610     /**
22611      * Usually called by the {@link Roo.data.Store} which owns the Record.
22612      * Commits all changes made to the Record since either creation, or the last commit operation.
22613      * <p>
22614      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22615      * of commit operations.
22616      */
22617     commit : function(){
22618         this.dirty = false;
22619         delete this.modified;
22620         this.editing = false;
22621         if(this.store){
22622             this.store.afterCommit(this);
22623         }
22624     },
22625
22626     // private
22627     hasError : function(){
22628         return this.error != null;
22629     },
22630
22631     // private
22632     clearError : function(){
22633         this.error = null;
22634     },
22635
22636     /**
22637      * Creates a copy of this record.
22638      * @param {String} id (optional) A new record id if you don't want to use this record's id
22639      * @return {Record}
22640      */
22641     copy : function(newId) {
22642         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22643     }
22644 };/*
22645  * Based on:
22646  * Ext JS Library 1.1.1
22647  * Copyright(c) 2006-2007, Ext JS, LLC.
22648  *
22649  * Originally Released Under LGPL - original licence link has changed is not relivant.
22650  *
22651  * Fork - LGPL
22652  * <script type="text/javascript">
22653  */
22654
22655
22656
22657 /**
22658  * @class Roo.data.Store
22659  * @extends Roo.util.Observable
22660  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22661  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22662  * <p>
22663  * 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
22664  * has no knowledge of the format of the data returned by the Proxy.<br>
22665  * <p>
22666  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22667  * instances from the data object. These records are cached and made available through accessor functions.
22668  * @constructor
22669  * Creates a new Store.
22670  * @param {Object} config A config object containing the objects needed for the Store to access data,
22671  * and read the data into Records.
22672  */
22673 Roo.data.Store = function(config){
22674     this.data = new Roo.util.MixedCollection(false);
22675     this.data.getKey = function(o){
22676         return o.id;
22677     };
22678     this.baseParams = {};
22679     // private
22680     this.paramNames = {
22681         "start" : "start",
22682         "limit" : "limit",
22683         "sort" : "sort",
22684         "dir" : "dir",
22685         "multisort" : "_multisort"
22686     };
22687
22688     if(config && config.data){
22689         this.inlineData = config.data;
22690         delete config.data;
22691     }
22692
22693     Roo.apply(this, config);
22694     
22695     if(this.reader){ // reader passed
22696         this.reader = Roo.factory(this.reader, Roo.data);
22697         this.reader.xmodule = this.xmodule || false;
22698         if(!this.recordType){
22699             this.recordType = this.reader.recordType;
22700         }
22701         if(this.reader.onMetaChange){
22702             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22703         }
22704     }
22705
22706     if(this.recordType){
22707         this.fields = this.recordType.prototype.fields;
22708     }
22709     this.modified = [];
22710
22711     this.addEvents({
22712         /**
22713          * @event datachanged
22714          * Fires when the data cache has changed, and a widget which is using this Store
22715          * as a Record cache should refresh its view.
22716          * @param {Store} this
22717          */
22718         datachanged : true,
22719         /**
22720          * @event metachange
22721          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22722          * @param {Store} this
22723          * @param {Object} meta The JSON metadata
22724          */
22725         metachange : true,
22726         /**
22727          * @event add
22728          * Fires when Records have been added to the Store
22729          * @param {Store} this
22730          * @param {Roo.data.Record[]} records The array of Records added
22731          * @param {Number} index The index at which the record(s) were added
22732          */
22733         add : true,
22734         /**
22735          * @event remove
22736          * Fires when a Record has been removed from the Store
22737          * @param {Store} this
22738          * @param {Roo.data.Record} record The Record that was removed
22739          * @param {Number} index The index at which the record was removed
22740          */
22741         remove : true,
22742         /**
22743          * @event update
22744          * Fires when a Record has been updated
22745          * @param {Store} this
22746          * @param {Roo.data.Record} record The Record that was updated
22747          * @param {String} operation The update operation being performed.  Value may be one of:
22748          * <pre><code>
22749  Roo.data.Record.EDIT
22750  Roo.data.Record.REJECT
22751  Roo.data.Record.COMMIT
22752          * </code></pre>
22753          */
22754         update : true,
22755         /**
22756          * @event clear
22757          * Fires when the data cache has been cleared.
22758          * @param {Store} this
22759          */
22760         clear : true,
22761         /**
22762          * @event beforeload
22763          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22764          * the load action will be canceled.
22765          * @param {Store} this
22766          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22767          */
22768         beforeload : true,
22769         /**
22770          * @event beforeloadadd
22771          * Fires after a new set of Records has been loaded.
22772          * @param {Store} this
22773          * @param {Roo.data.Record[]} records The Records that were loaded
22774          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22775          */
22776         beforeloadadd : true,
22777         /**
22778          * @event load
22779          * Fires after a new set of Records has been loaded, before they are added to the store.
22780          * @param {Store} this
22781          * @param {Roo.data.Record[]} records The Records that were loaded
22782          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22783          * @params {Object} return from reader
22784          */
22785         load : true,
22786         /**
22787          * @event loadexception
22788          * Fires if an exception occurs in the Proxy during loading.
22789          * Called with the signature of the Proxy's "loadexception" event.
22790          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22791          * 
22792          * @param {Proxy} 
22793          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22794          * @param {Object} load options 
22795          * @param {Object} jsonData from your request (normally this contains the Exception)
22796          */
22797         loadexception : true
22798     });
22799     
22800     if(this.proxy){
22801         this.proxy = Roo.factory(this.proxy, Roo.data);
22802         this.proxy.xmodule = this.xmodule || false;
22803         this.relayEvents(this.proxy,  ["loadexception"]);
22804     }
22805     this.sortToggle = {};
22806     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22807
22808     Roo.data.Store.superclass.constructor.call(this);
22809
22810     if(this.inlineData){
22811         this.loadData(this.inlineData);
22812         delete this.inlineData;
22813     }
22814 };
22815
22816 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22817      /**
22818     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22819     * without a remote query - used by combo/forms at present.
22820     */
22821     
22822     /**
22823     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22824     */
22825     /**
22826     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22827     */
22828     /**
22829     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22830     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22831     */
22832     /**
22833     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22834     * on any HTTP request
22835     */
22836     /**
22837     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22838     */
22839     /**
22840     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22841     */
22842     multiSort: false,
22843     /**
22844     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22845     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22846     */
22847     remoteSort : false,
22848
22849     /**
22850     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22851      * loaded or when a record is removed. (defaults to false).
22852     */
22853     pruneModifiedRecords : false,
22854
22855     // private
22856     lastOptions : null,
22857
22858     /**
22859      * Add Records to the Store and fires the add event.
22860      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22861      */
22862     add : function(records){
22863         records = [].concat(records);
22864         for(var i = 0, len = records.length; i < len; i++){
22865             records[i].join(this);
22866         }
22867         var index = this.data.length;
22868         this.data.addAll(records);
22869         this.fireEvent("add", this, records, index);
22870     },
22871
22872     /**
22873      * Remove a Record from the Store and fires the remove event.
22874      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22875      */
22876     remove : function(record){
22877         var index = this.data.indexOf(record);
22878         this.data.removeAt(index);
22879         if(this.pruneModifiedRecords){
22880             this.modified.remove(record);
22881         }
22882         this.fireEvent("remove", this, record, index);
22883     },
22884
22885     /**
22886      * Remove all Records from the Store and fires the clear event.
22887      */
22888     removeAll : function(){
22889         this.data.clear();
22890         if(this.pruneModifiedRecords){
22891             this.modified = [];
22892         }
22893         this.fireEvent("clear", this);
22894     },
22895
22896     /**
22897      * Inserts Records to the Store at the given index and fires the add event.
22898      * @param {Number} index The start index at which to insert the passed Records.
22899      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22900      */
22901     insert : function(index, records){
22902         records = [].concat(records);
22903         for(var i = 0, len = records.length; i < len; i++){
22904             this.data.insert(index, records[i]);
22905             records[i].join(this);
22906         }
22907         this.fireEvent("add", this, records, index);
22908     },
22909
22910     /**
22911      * Get the index within the cache of the passed Record.
22912      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22913      * @return {Number} The index of the passed Record. Returns -1 if not found.
22914      */
22915     indexOf : function(record){
22916         return this.data.indexOf(record);
22917     },
22918
22919     /**
22920      * Get the index within the cache of the Record with the passed id.
22921      * @param {String} id The id of the Record to find.
22922      * @return {Number} The index of the Record. Returns -1 if not found.
22923      */
22924     indexOfId : function(id){
22925         return this.data.indexOfKey(id);
22926     },
22927
22928     /**
22929      * Get the Record with the specified id.
22930      * @param {String} id The id of the Record to find.
22931      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22932      */
22933     getById : function(id){
22934         return this.data.key(id);
22935     },
22936
22937     /**
22938      * Get the Record at the specified index.
22939      * @param {Number} index The index of the Record to find.
22940      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22941      */
22942     getAt : function(index){
22943         return this.data.itemAt(index);
22944     },
22945
22946     /**
22947      * Returns a range of Records between specified indices.
22948      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22949      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22950      * @return {Roo.data.Record[]} An array of Records
22951      */
22952     getRange : function(start, end){
22953         return this.data.getRange(start, end);
22954     },
22955
22956     // private
22957     storeOptions : function(o){
22958         o = Roo.apply({}, o);
22959         delete o.callback;
22960         delete o.scope;
22961         this.lastOptions = o;
22962     },
22963
22964     /**
22965      * Loads the Record cache from the configured Proxy using the configured Reader.
22966      * <p>
22967      * If using remote paging, then the first load call must specify the <em>start</em>
22968      * and <em>limit</em> properties in the options.params property to establish the initial
22969      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22970      * <p>
22971      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22972      * and this call will return before the new data has been loaded. Perform any post-processing
22973      * in a callback function, or in a "load" event handler.</strong>
22974      * <p>
22975      * @param {Object} options An object containing properties which control loading options:<ul>
22976      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22977      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22978      * passed the following arguments:<ul>
22979      * <li>r : Roo.data.Record[]</li>
22980      * <li>options: Options object from the load call</li>
22981      * <li>success: Boolean success indicator</li></ul></li>
22982      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22983      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22984      * </ul>
22985      */
22986     load : function(options){
22987         options = options || {};
22988         if(this.fireEvent("beforeload", this, options) !== false){
22989             this.storeOptions(options);
22990             var p = Roo.apply(options.params || {}, this.baseParams);
22991             // if meta was not loaded from remote source.. try requesting it.
22992             if (!this.reader.metaFromRemote) {
22993                 p._requestMeta = 1;
22994             }
22995             if(this.sortInfo && this.remoteSort){
22996                 var pn = this.paramNames;
22997                 p[pn["sort"]] = this.sortInfo.field;
22998                 p[pn["dir"]] = this.sortInfo.direction;
22999             }
23000             if (this.multiSort) {
23001                 var pn = this.paramNames;
23002                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23003             }
23004             
23005             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23006         }
23007     },
23008
23009     /**
23010      * Reloads the Record cache from the configured Proxy using the configured Reader and
23011      * the options from the last load operation performed.
23012      * @param {Object} options (optional) An object containing properties which may override the options
23013      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23014      * the most recently used options are reused).
23015      */
23016     reload : function(options){
23017         this.load(Roo.applyIf(options||{}, this.lastOptions));
23018     },
23019
23020     // private
23021     // Called as a callback by the Reader during a load operation.
23022     loadRecords : function(o, options, success){
23023         if(!o || success === false){
23024             if(success !== false){
23025                 this.fireEvent("load", this, [], options, o);
23026             }
23027             if(options.callback){
23028                 options.callback.call(options.scope || this, [], options, false);
23029             }
23030             return;
23031         }
23032         // if data returned failure - throw an exception.
23033         if (o.success === false) {
23034             // show a message if no listener is registered.
23035             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23036                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23037             }
23038             // loadmask wil be hooked into this..
23039             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23040             return;
23041         }
23042         var r = o.records, t = o.totalRecords || r.length;
23043         
23044         this.fireEvent("beforeloadadd", this, r, options, o);
23045         
23046         if(!options || options.add !== true){
23047             if(this.pruneModifiedRecords){
23048                 this.modified = [];
23049             }
23050             for(var i = 0, len = r.length; i < len; i++){
23051                 r[i].join(this);
23052             }
23053             if(this.snapshot){
23054                 this.data = this.snapshot;
23055                 delete this.snapshot;
23056             }
23057             this.data.clear();
23058             this.data.addAll(r);
23059             this.totalLength = t;
23060             this.applySort();
23061             this.fireEvent("datachanged", this);
23062         }else{
23063             this.totalLength = Math.max(t, this.data.length+r.length);
23064             this.add(r);
23065         }
23066         this.fireEvent("load", this, r, options, o);
23067         if(options.callback){
23068             options.callback.call(options.scope || this, r, options, true);
23069         }
23070     },
23071
23072
23073     /**
23074      * Loads data from a passed data block. A Reader which understands the format of the data
23075      * must have been configured in the constructor.
23076      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23077      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23078      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23079      */
23080     loadData : function(o, append){
23081         var r = this.reader.readRecords(o);
23082         this.loadRecords(r, {add: append}, true);
23083     },
23084
23085     /**
23086      * Gets the number of cached records.
23087      * <p>
23088      * <em>If using paging, this may not be the total size of the dataset. If the data object
23089      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23090      * the data set size</em>
23091      */
23092     getCount : function(){
23093         return this.data.length || 0;
23094     },
23095
23096     /**
23097      * Gets the total number of records in the dataset as returned by the server.
23098      * <p>
23099      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23100      * the dataset size</em>
23101      */
23102     getTotalCount : function(){
23103         return this.totalLength || 0;
23104     },
23105
23106     /**
23107      * Returns the sort state of the Store as an object with two properties:
23108      * <pre><code>
23109  field {String} The name of the field by which the Records are sorted
23110  direction {String} The sort order, "ASC" or "DESC"
23111      * </code></pre>
23112      */
23113     getSortState : function(){
23114         return this.sortInfo;
23115     },
23116
23117     // private
23118     applySort : function(){
23119         if(this.sortInfo && !this.remoteSort){
23120             var s = this.sortInfo, f = s.field;
23121             var st = this.fields.get(f).sortType;
23122             var fn = function(r1, r2){
23123                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23124                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23125             };
23126             this.data.sort(s.direction, fn);
23127             if(this.snapshot && this.snapshot != this.data){
23128                 this.snapshot.sort(s.direction, fn);
23129             }
23130         }
23131     },
23132
23133     /**
23134      * Sets the default sort column and order to be used by the next load operation.
23135      * @param {String} fieldName The name of the field to sort by.
23136      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23137      */
23138     setDefaultSort : function(field, dir){
23139         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23140     },
23141
23142     /**
23143      * Sort the Records.
23144      * If remote sorting is used, the sort is performed on the server, and the cache is
23145      * reloaded. If local sorting is used, the cache is sorted internally.
23146      * @param {String} fieldName The name of the field to sort by.
23147      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23148      */
23149     sort : function(fieldName, dir){
23150         var f = this.fields.get(fieldName);
23151         if(!dir){
23152             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23153             
23154             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23155                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23156             }else{
23157                 dir = f.sortDir;
23158             }
23159         }
23160         this.sortToggle[f.name] = dir;
23161         this.sortInfo = {field: f.name, direction: dir};
23162         if(!this.remoteSort){
23163             this.applySort();
23164             this.fireEvent("datachanged", this);
23165         }else{
23166             this.load(this.lastOptions);
23167         }
23168     },
23169
23170     /**
23171      * Calls the specified function for each of the Records in the cache.
23172      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23173      * Returning <em>false</em> aborts and exits the iteration.
23174      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23175      */
23176     each : function(fn, scope){
23177         this.data.each(fn, scope);
23178     },
23179
23180     /**
23181      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23182      * (e.g., during paging).
23183      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23184      */
23185     getModifiedRecords : function(){
23186         return this.modified;
23187     },
23188
23189     // private
23190     createFilterFn : function(property, value, anyMatch){
23191         if(!value.exec){ // not a regex
23192             value = String(value);
23193             if(value.length == 0){
23194                 return false;
23195             }
23196             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23197         }
23198         return function(r){
23199             return value.test(r.data[property]);
23200         };
23201     },
23202
23203     /**
23204      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23205      * @param {String} property A field on your records
23206      * @param {Number} start The record index to start at (defaults to 0)
23207      * @param {Number} end The last record index to include (defaults to length - 1)
23208      * @return {Number} The sum
23209      */
23210     sum : function(property, start, end){
23211         var rs = this.data.items, v = 0;
23212         start = start || 0;
23213         end = (end || end === 0) ? end : rs.length-1;
23214
23215         for(var i = start; i <= end; i++){
23216             v += (rs[i].data[property] || 0);
23217         }
23218         return v;
23219     },
23220
23221     /**
23222      * Filter the records by a specified property.
23223      * @param {String} field A field on your records
23224      * @param {String/RegExp} value Either a string that the field
23225      * should start with or a RegExp to test against the field
23226      * @param {Boolean} anyMatch True to match any part not just the beginning
23227      */
23228     filter : function(property, value, anyMatch){
23229         var fn = this.createFilterFn(property, value, anyMatch);
23230         return fn ? this.filterBy(fn) : this.clearFilter();
23231     },
23232
23233     /**
23234      * Filter by a function. The specified function will be called with each
23235      * record in this data source. If the function returns true the record is included,
23236      * otherwise it is filtered.
23237      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23238      * @param {Object} scope (optional) The scope of the function (defaults to this)
23239      */
23240     filterBy : function(fn, scope){
23241         this.snapshot = this.snapshot || this.data;
23242         this.data = this.queryBy(fn, scope||this);
23243         this.fireEvent("datachanged", this);
23244     },
23245
23246     /**
23247      * Query the records by a specified property.
23248      * @param {String} field A field on your records
23249      * @param {String/RegExp} value Either a string that the field
23250      * should start with or a RegExp to test against the field
23251      * @param {Boolean} anyMatch True to match any part not just the beginning
23252      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23253      */
23254     query : function(property, value, anyMatch){
23255         var fn = this.createFilterFn(property, value, anyMatch);
23256         return fn ? this.queryBy(fn) : this.data.clone();
23257     },
23258
23259     /**
23260      * Query by a function. The specified function will be called with each
23261      * record in this data source. If the function returns true the record is included
23262      * in the results.
23263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23264      * @param {Object} scope (optional) The scope of the function (defaults to this)
23265       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23266      **/
23267     queryBy : function(fn, scope){
23268         var data = this.snapshot || this.data;
23269         return data.filterBy(fn, scope||this);
23270     },
23271
23272     /**
23273      * Collects unique values for a particular dataIndex from this store.
23274      * @param {String} dataIndex The property to collect
23275      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23276      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23277      * @return {Array} An array of the unique values
23278      **/
23279     collect : function(dataIndex, allowNull, bypassFilter){
23280         var d = (bypassFilter === true && this.snapshot) ?
23281                 this.snapshot.items : this.data.items;
23282         var v, sv, r = [], l = {};
23283         for(var i = 0, len = d.length; i < len; i++){
23284             v = d[i].data[dataIndex];
23285             sv = String(v);
23286             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23287                 l[sv] = true;
23288                 r[r.length] = v;
23289             }
23290         }
23291         return r;
23292     },
23293
23294     /**
23295      * Revert to a view of the Record cache with no filtering applied.
23296      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23297      */
23298     clearFilter : function(suppressEvent){
23299         if(this.snapshot && this.snapshot != this.data){
23300             this.data = this.snapshot;
23301             delete this.snapshot;
23302             if(suppressEvent !== true){
23303                 this.fireEvent("datachanged", this);
23304             }
23305         }
23306     },
23307
23308     // private
23309     afterEdit : function(record){
23310         if(this.modified.indexOf(record) == -1){
23311             this.modified.push(record);
23312         }
23313         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23314     },
23315     
23316     // private
23317     afterReject : function(record){
23318         this.modified.remove(record);
23319         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23320     },
23321
23322     // private
23323     afterCommit : function(record){
23324         this.modified.remove(record);
23325         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23326     },
23327
23328     /**
23329      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23330      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23331      */
23332     commitChanges : function(){
23333         var m = this.modified.slice(0);
23334         this.modified = [];
23335         for(var i = 0, len = m.length; i < len; i++){
23336             m[i].commit();
23337         }
23338     },
23339
23340     /**
23341      * Cancel outstanding changes on all changed records.
23342      */
23343     rejectChanges : function(){
23344         var m = this.modified.slice(0);
23345         this.modified = [];
23346         for(var i = 0, len = m.length; i < len; i++){
23347             m[i].reject();
23348         }
23349     },
23350
23351     onMetaChange : function(meta, rtype, o){
23352         this.recordType = rtype;
23353         this.fields = rtype.prototype.fields;
23354         delete this.snapshot;
23355         this.sortInfo = meta.sortInfo || this.sortInfo;
23356         this.modified = [];
23357         this.fireEvent('metachange', this, this.reader.meta);
23358     },
23359     
23360     moveIndex : function(data, type)
23361     {
23362         var index = this.indexOf(data);
23363         
23364         var newIndex = index + type;
23365         
23366         this.remove(data);
23367         
23368         this.insert(newIndex, data);
23369         
23370     }
23371 });/*
23372  * Based on:
23373  * Ext JS Library 1.1.1
23374  * Copyright(c) 2006-2007, Ext JS, LLC.
23375  *
23376  * Originally Released Under LGPL - original licence link has changed is not relivant.
23377  *
23378  * Fork - LGPL
23379  * <script type="text/javascript">
23380  */
23381
23382 /**
23383  * @class Roo.data.SimpleStore
23384  * @extends Roo.data.Store
23385  * Small helper class to make creating Stores from Array data easier.
23386  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23387  * @cfg {Array} fields An array of field definition objects, or field name strings.
23388  * @cfg {Array} data The multi-dimensional array of data
23389  * @constructor
23390  * @param {Object} config
23391  */
23392 Roo.data.SimpleStore = function(config){
23393     Roo.data.SimpleStore.superclass.constructor.call(this, {
23394         isLocal : true,
23395         reader: new Roo.data.ArrayReader({
23396                 id: config.id
23397             },
23398             Roo.data.Record.create(config.fields)
23399         ),
23400         proxy : new Roo.data.MemoryProxy(config.data)
23401     });
23402     this.load();
23403 };
23404 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23405  * Based on:
23406  * Ext JS Library 1.1.1
23407  * Copyright(c) 2006-2007, Ext JS, LLC.
23408  *
23409  * Originally Released Under LGPL - original licence link has changed is not relivant.
23410  *
23411  * Fork - LGPL
23412  * <script type="text/javascript">
23413  */
23414
23415 /**
23416 /**
23417  * @extends Roo.data.Store
23418  * @class Roo.data.JsonStore
23419  * Small helper class to make creating Stores for JSON data easier. <br/>
23420 <pre><code>
23421 var store = new Roo.data.JsonStore({
23422     url: 'get-images.php',
23423     root: 'images',
23424     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23425 });
23426 </code></pre>
23427  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23428  * JsonReader and HttpProxy (unless inline data is provided).</b>
23429  * @cfg {Array} fields An array of field definition objects, or field name strings.
23430  * @constructor
23431  * @param {Object} config
23432  */
23433 Roo.data.JsonStore = function(c){
23434     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23435         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23436         reader: new Roo.data.JsonReader(c, c.fields)
23437     }));
23438 };
23439 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23440  * Based on:
23441  * Ext JS Library 1.1.1
23442  * Copyright(c) 2006-2007, Ext JS, LLC.
23443  *
23444  * Originally Released Under LGPL - original licence link has changed is not relivant.
23445  *
23446  * Fork - LGPL
23447  * <script type="text/javascript">
23448  */
23449
23450  
23451 Roo.data.Field = function(config){
23452     if(typeof config == "string"){
23453         config = {name: config};
23454     }
23455     Roo.apply(this, config);
23456     
23457     if(!this.type){
23458         this.type = "auto";
23459     }
23460     
23461     var st = Roo.data.SortTypes;
23462     // named sortTypes are supported, here we look them up
23463     if(typeof this.sortType == "string"){
23464         this.sortType = st[this.sortType];
23465     }
23466     
23467     // set default sortType for strings and dates
23468     if(!this.sortType){
23469         switch(this.type){
23470             case "string":
23471                 this.sortType = st.asUCString;
23472                 break;
23473             case "date":
23474                 this.sortType = st.asDate;
23475                 break;
23476             default:
23477                 this.sortType = st.none;
23478         }
23479     }
23480
23481     // define once
23482     var stripRe = /[\$,%]/g;
23483
23484     // prebuilt conversion function for this field, instead of
23485     // switching every time we're reading a value
23486     if(!this.convert){
23487         var cv, dateFormat = this.dateFormat;
23488         switch(this.type){
23489             case "":
23490             case "auto":
23491             case undefined:
23492                 cv = function(v){ return v; };
23493                 break;
23494             case "string":
23495                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23496                 break;
23497             case "int":
23498                 cv = function(v){
23499                     return v !== undefined && v !== null && v !== '' ?
23500                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23501                     };
23502                 break;
23503             case "float":
23504                 cv = function(v){
23505                     return v !== undefined && v !== null && v !== '' ?
23506                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23507                     };
23508                 break;
23509             case "bool":
23510             case "boolean":
23511                 cv = function(v){ return v === true || v === "true" || v == 1; };
23512                 break;
23513             case "date":
23514                 cv = function(v){
23515                     if(!v){
23516                         return '';
23517                     }
23518                     if(v instanceof Date){
23519                         return v;
23520                     }
23521                     if(dateFormat){
23522                         if(dateFormat == "timestamp"){
23523                             return new Date(v*1000);
23524                         }
23525                         return Date.parseDate(v, dateFormat);
23526                     }
23527                     var parsed = Date.parse(v);
23528                     return parsed ? new Date(parsed) : null;
23529                 };
23530              break;
23531             
23532         }
23533         this.convert = cv;
23534     }
23535 };
23536
23537 Roo.data.Field.prototype = {
23538     dateFormat: null,
23539     defaultValue: "",
23540     mapping: null,
23541     sortType : null,
23542     sortDir : "ASC"
23543 };/*
23544  * Based on:
23545  * Ext JS Library 1.1.1
23546  * Copyright(c) 2006-2007, Ext JS, LLC.
23547  *
23548  * Originally Released Under LGPL - original licence link has changed is not relivant.
23549  *
23550  * Fork - LGPL
23551  * <script type="text/javascript">
23552  */
23553  
23554 // Base class for reading structured data from a data source.  This class is intended to be
23555 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23556
23557 /**
23558  * @class Roo.data.DataReader
23559  * Base class for reading structured data from a data source.  This class is intended to be
23560  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23561  */
23562
23563 Roo.data.DataReader = function(meta, recordType){
23564     
23565     this.meta = meta;
23566     
23567     this.recordType = recordType instanceof Array ? 
23568         Roo.data.Record.create(recordType) : recordType;
23569 };
23570
23571 Roo.data.DataReader.prototype = {
23572      /**
23573      * Create an empty record
23574      * @param {Object} data (optional) - overlay some values
23575      * @return {Roo.data.Record} record created.
23576      */
23577     newRow :  function(d) {
23578         var da =  {};
23579         this.recordType.prototype.fields.each(function(c) {
23580             switch( c.type) {
23581                 case 'int' : da[c.name] = 0; break;
23582                 case 'date' : da[c.name] = new Date(); break;
23583                 case 'float' : da[c.name] = 0.0; break;
23584                 case 'boolean' : da[c.name] = false; break;
23585                 default : da[c.name] = ""; break;
23586             }
23587             
23588         });
23589         return new this.recordType(Roo.apply(da, d));
23590     }
23591     
23592 };/*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602
23603 /**
23604  * @class Roo.data.DataProxy
23605  * @extends Roo.data.Observable
23606  * This class is an abstract base class for implementations which provide retrieval of
23607  * unformatted data objects.<br>
23608  * <p>
23609  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23610  * (of the appropriate type which knows how to parse the data object) to provide a block of
23611  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23612  * <p>
23613  * Custom implementations must implement the load method as described in
23614  * {@link Roo.data.HttpProxy#load}.
23615  */
23616 Roo.data.DataProxy = function(){
23617     this.addEvents({
23618         /**
23619          * @event beforeload
23620          * Fires before a network request is made to retrieve a data object.
23621          * @param {Object} This DataProxy object.
23622          * @param {Object} params The params parameter to the load function.
23623          */
23624         beforeload : true,
23625         /**
23626          * @event load
23627          * Fires before the load method's callback is called.
23628          * @param {Object} This DataProxy object.
23629          * @param {Object} o The data object.
23630          * @param {Object} arg The callback argument object passed to the load function.
23631          */
23632         load : true,
23633         /**
23634          * @event loadexception
23635          * Fires if an Exception occurs during data retrieval.
23636          * @param {Object} This DataProxy object.
23637          * @param {Object} o The data object.
23638          * @param {Object} arg The callback argument object passed to the load function.
23639          * @param {Object} e The Exception.
23640          */
23641         loadexception : true
23642     });
23643     Roo.data.DataProxy.superclass.constructor.call(this);
23644 };
23645
23646 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23647
23648     /**
23649      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23650      */
23651 /*
23652  * Based on:
23653  * Ext JS Library 1.1.1
23654  * Copyright(c) 2006-2007, Ext JS, LLC.
23655  *
23656  * Originally Released Under LGPL - original licence link has changed is not relivant.
23657  *
23658  * Fork - LGPL
23659  * <script type="text/javascript">
23660  */
23661 /**
23662  * @class Roo.data.MemoryProxy
23663  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23664  * to the Reader when its load method is called.
23665  * @constructor
23666  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23667  */
23668 Roo.data.MemoryProxy = function(data){
23669     if (data.data) {
23670         data = data.data;
23671     }
23672     Roo.data.MemoryProxy.superclass.constructor.call(this);
23673     this.data = data;
23674 };
23675
23676 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23677     
23678     /**
23679      * Load data from the requested source (in this case an in-memory
23680      * data object passed to the constructor), read the data object into
23681      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23682      * process that block using the passed callback.
23683      * @param {Object} params This parameter is not used by the MemoryProxy class.
23684      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23685      * object into a block of Roo.data.Records.
23686      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23687      * The function must be passed <ul>
23688      * <li>The Record block object</li>
23689      * <li>The "arg" argument from the load function</li>
23690      * <li>A boolean success indicator</li>
23691      * </ul>
23692      * @param {Object} scope The scope in which to call the callback
23693      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23694      */
23695     load : function(params, reader, callback, scope, arg){
23696         params = params || {};
23697         var result;
23698         try {
23699             result = reader.readRecords(this.data);
23700         }catch(e){
23701             this.fireEvent("loadexception", this, arg, null, e);
23702             callback.call(scope, null, arg, false);
23703             return;
23704         }
23705         callback.call(scope, result, arg, true);
23706     },
23707     
23708     // private
23709     update : function(params, records){
23710         
23711     }
23712 });/*
23713  * Based on:
23714  * Ext JS Library 1.1.1
23715  * Copyright(c) 2006-2007, Ext JS, LLC.
23716  *
23717  * Originally Released Under LGPL - original licence link has changed is not relivant.
23718  *
23719  * Fork - LGPL
23720  * <script type="text/javascript">
23721  */
23722 /**
23723  * @class Roo.data.HttpProxy
23724  * @extends Roo.data.DataProxy
23725  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23726  * configured to reference a certain URL.<br><br>
23727  * <p>
23728  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23729  * from which the running page was served.<br><br>
23730  * <p>
23731  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23732  * <p>
23733  * Be aware that to enable the browser to parse an XML document, the server must set
23734  * the Content-Type header in the HTTP response to "text/xml".
23735  * @constructor
23736  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23737  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23738  * will be used to make the request.
23739  */
23740 Roo.data.HttpProxy = function(conn){
23741     Roo.data.HttpProxy.superclass.constructor.call(this);
23742     // is conn a conn config or a real conn?
23743     this.conn = conn;
23744     this.useAjax = !conn || !conn.events;
23745   
23746 };
23747
23748 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23749     // thse are take from connection...
23750     
23751     /**
23752      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23753      */
23754     /**
23755      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23756      * extra parameters to each request made by this object. (defaults to undefined)
23757      */
23758     /**
23759      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23760      *  to each request made by this object. (defaults to undefined)
23761      */
23762     /**
23763      * @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)
23764      */
23765     /**
23766      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23767      */
23768      /**
23769      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23770      * @type Boolean
23771      */
23772   
23773
23774     /**
23775      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23776      * @type Boolean
23777      */
23778     /**
23779      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23780      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23781      * a finer-grained basis than the DataProxy events.
23782      */
23783     getConnection : function(){
23784         return this.useAjax ? Roo.Ajax : this.conn;
23785     },
23786
23787     /**
23788      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23789      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23790      * process that block using the passed callback.
23791      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23792      * for the request to the remote server.
23793      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23794      * object into a block of Roo.data.Records.
23795      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23796      * The function must be passed <ul>
23797      * <li>The Record block object</li>
23798      * <li>The "arg" argument from the load function</li>
23799      * <li>A boolean success indicator</li>
23800      * </ul>
23801      * @param {Object} scope The scope in which to call the callback
23802      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23803      */
23804     load : function(params, reader, callback, scope, arg){
23805         if(this.fireEvent("beforeload", this, params) !== false){
23806             var  o = {
23807                 params : params || {},
23808                 request: {
23809                     callback : callback,
23810                     scope : scope,
23811                     arg : arg
23812                 },
23813                 reader: reader,
23814                 callback : this.loadResponse,
23815                 scope: this
23816             };
23817             if(this.useAjax){
23818                 Roo.applyIf(o, this.conn);
23819                 if(this.activeRequest){
23820                     Roo.Ajax.abort(this.activeRequest);
23821                 }
23822                 this.activeRequest = Roo.Ajax.request(o);
23823             }else{
23824                 this.conn.request(o);
23825             }
23826         }else{
23827             callback.call(scope||this, null, arg, false);
23828         }
23829     },
23830
23831     // private
23832     loadResponse : function(o, success, response){
23833         delete this.activeRequest;
23834         if(!success){
23835             this.fireEvent("loadexception", this, o, response);
23836             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23837             return;
23838         }
23839         var result;
23840         try {
23841             result = o.reader.read(response);
23842         }catch(e){
23843             this.fireEvent("loadexception", this, o, response, e);
23844             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23845             return;
23846         }
23847         
23848         this.fireEvent("load", this, o, o.request.arg);
23849         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23850     },
23851
23852     // private
23853     update : function(dataSet){
23854
23855     },
23856
23857     // private
23858     updateResponse : function(dataSet){
23859
23860     }
23861 });/*
23862  * Based on:
23863  * Ext JS Library 1.1.1
23864  * Copyright(c) 2006-2007, Ext JS, LLC.
23865  *
23866  * Originally Released Under LGPL - original licence link has changed is not relivant.
23867  *
23868  * Fork - LGPL
23869  * <script type="text/javascript">
23870  */
23871
23872 /**
23873  * @class Roo.data.ScriptTagProxy
23874  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23875  * other than the originating domain of the running page.<br><br>
23876  * <p>
23877  * <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
23878  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23879  * <p>
23880  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23881  * source code that is used as the source inside a &lt;script> tag.<br><br>
23882  * <p>
23883  * In order for the browser to process the returned data, the server must wrap the data object
23884  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23885  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23886  * depending on whether the callback name was passed:
23887  * <p>
23888  * <pre><code>
23889 boolean scriptTag = false;
23890 String cb = request.getParameter("callback");
23891 if (cb != null) {
23892     scriptTag = true;
23893     response.setContentType("text/javascript");
23894 } else {
23895     response.setContentType("application/x-json");
23896 }
23897 Writer out = response.getWriter();
23898 if (scriptTag) {
23899     out.write(cb + "(");
23900 }
23901 out.print(dataBlock.toJsonString());
23902 if (scriptTag) {
23903     out.write(");");
23904 }
23905 </pre></code>
23906  *
23907  * @constructor
23908  * @param {Object} config A configuration object.
23909  */
23910 Roo.data.ScriptTagProxy = function(config){
23911     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23912     Roo.apply(this, config);
23913     this.head = document.getElementsByTagName("head")[0];
23914 };
23915
23916 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23917
23918 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23919     /**
23920      * @cfg {String} url The URL from which to request the data object.
23921      */
23922     /**
23923      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23924      */
23925     timeout : 30000,
23926     /**
23927      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23928      * the server the name of the callback function set up by the load call to process the returned data object.
23929      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23930      * javascript output which calls this named function passing the data object as its only parameter.
23931      */
23932     callbackParam : "callback",
23933     /**
23934      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23935      * name to the request.
23936      */
23937     nocache : true,
23938
23939     /**
23940      * Load data from the configured URL, read the data object into
23941      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23942      * process that block using the passed callback.
23943      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23944      * for the request to the remote server.
23945      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23946      * object into a block of Roo.data.Records.
23947      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23948      * The function must be passed <ul>
23949      * <li>The Record block object</li>
23950      * <li>The "arg" argument from the load function</li>
23951      * <li>A boolean success indicator</li>
23952      * </ul>
23953      * @param {Object} scope The scope in which to call the callback
23954      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23955      */
23956     load : function(params, reader, callback, scope, arg){
23957         if(this.fireEvent("beforeload", this, params) !== false){
23958
23959             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23960
23961             var url = this.url;
23962             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23963             if(this.nocache){
23964                 url += "&_dc=" + (new Date().getTime());
23965             }
23966             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23967             var trans = {
23968                 id : transId,
23969                 cb : "stcCallback"+transId,
23970                 scriptId : "stcScript"+transId,
23971                 params : params,
23972                 arg : arg,
23973                 url : url,
23974                 callback : callback,
23975                 scope : scope,
23976                 reader : reader
23977             };
23978             var conn = this;
23979
23980             window[trans.cb] = function(o){
23981                 conn.handleResponse(o, trans);
23982             };
23983
23984             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23985
23986             if(this.autoAbort !== false){
23987                 this.abort();
23988             }
23989
23990             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23991
23992             var script = document.createElement("script");
23993             script.setAttribute("src", url);
23994             script.setAttribute("type", "text/javascript");
23995             script.setAttribute("id", trans.scriptId);
23996             this.head.appendChild(script);
23997
23998             this.trans = trans;
23999         }else{
24000             callback.call(scope||this, null, arg, false);
24001         }
24002     },
24003
24004     // private
24005     isLoading : function(){
24006         return this.trans ? true : false;
24007     },
24008
24009     /**
24010      * Abort the current server request.
24011      */
24012     abort : function(){
24013         if(this.isLoading()){
24014             this.destroyTrans(this.trans);
24015         }
24016     },
24017
24018     // private
24019     destroyTrans : function(trans, isLoaded){
24020         this.head.removeChild(document.getElementById(trans.scriptId));
24021         clearTimeout(trans.timeoutId);
24022         if(isLoaded){
24023             window[trans.cb] = undefined;
24024             try{
24025                 delete window[trans.cb];
24026             }catch(e){}
24027         }else{
24028             // if hasn't been loaded, wait for load to remove it to prevent script error
24029             window[trans.cb] = function(){
24030                 window[trans.cb] = undefined;
24031                 try{
24032                     delete window[trans.cb];
24033                 }catch(e){}
24034             };
24035         }
24036     },
24037
24038     // private
24039     handleResponse : function(o, trans){
24040         this.trans = false;
24041         this.destroyTrans(trans, true);
24042         var result;
24043         try {
24044             result = trans.reader.readRecords(o);
24045         }catch(e){
24046             this.fireEvent("loadexception", this, o, trans.arg, e);
24047             trans.callback.call(trans.scope||window, null, trans.arg, false);
24048             return;
24049         }
24050         this.fireEvent("load", this, o, trans.arg);
24051         trans.callback.call(trans.scope||window, result, trans.arg, true);
24052     },
24053
24054     // private
24055     handleFailure : function(trans){
24056         this.trans = false;
24057         this.destroyTrans(trans, false);
24058         this.fireEvent("loadexception", this, null, trans.arg);
24059         trans.callback.call(trans.scope||window, null, trans.arg, false);
24060     }
24061 });/*
24062  * Based on:
24063  * Ext JS Library 1.1.1
24064  * Copyright(c) 2006-2007, Ext JS, LLC.
24065  *
24066  * Originally Released Under LGPL - original licence link has changed is not relivant.
24067  *
24068  * Fork - LGPL
24069  * <script type="text/javascript">
24070  */
24071
24072 /**
24073  * @class Roo.data.JsonReader
24074  * @extends Roo.data.DataReader
24075  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24076  * based on mappings in a provided Roo.data.Record constructor.
24077  * 
24078  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24079  * in the reply previously. 
24080  * 
24081  * <p>
24082  * Example code:
24083  * <pre><code>
24084 var RecordDef = Roo.data.Record.create([
24085     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24086     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24087 ]);
24088 var myReader = new Roo.data.JsonReader({
24089     totalProperty: "results",    // The property which contains the total dataset size (optional)
24090     root: "rows",                // The property which contains an Array of row objects
24091     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24092 }, RecordDef);
24093 </code></pre>
24094  * <p>
24095  * This would consume a JSON file like this:
24096  * <pre><code>
24097 { 'results': 2, 'rows': [
24098     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24099     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24100 }
24101 </code></pre>
24102  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24103  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24104  * paged from the remote server.
24105  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24106  * @cfg {String} root name of the property which contains the Array of row objects.
24107  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24108  * @cfg {Array} fields Array of field definition objects
24109  * @constructor
24110  * Create a new JsonReader
24111  * @param {Object} meta Metadata configuration options
24112  * @param {Object} recordType Either an Array of field definition objects,
24113  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24114  */
24115 Roo.data.JsonReader = function(meta, recordType){
24116     
24117     meta = meta || {};
24118     // set some defaults:
24119     Roo.applyIf(meta, {
24120         totalProperty: 'total',
24121         successProperty : 'success',
24122         root : 'data',
24123         id : 'id'
24124     });
24125     
24126     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24127 };
24128 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24129     
24130     /**
24131      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24132      * Used by Store query builder to append _requestMeta to params.
24133      * 
24134      */
24135     metaFromRemote : false,
24136     /**
24137      * This method is only used by a DataProxy which has retrieved data from a remote server.
24138      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24139      * @return {Object} data A data block which is used by an Roo.data.Store object as
24140      * a cache of Roo.data.Records.
24141      */
24142     read : function(response){
24143         var json = response.responseText;
24144        
24145         var o = /* eval:var:o */ eval("("+json+")");
24146         if(!o) {
24147             throw {message: "JsonReader.read: Json object not found"};
24148         }
24149         
24150         if(o.metaData){
24151             
24152             delete this.ef;
24153             this.metaFromRemote = true;
24154             this.meta = o.metaData;
24155             this.recordType = Roo.data.Record.create(o.metaData.fields);
24156             this.onMetaChange(this.meta, this.recordType, o);
24157         }
24158         return this.readRecords(o);
24159     },
24160
24161     // private function a store will implement
24162     onMetaChange : function(meta, recordType, o){
24163
24164     },
24165
24166     /**
24167          * @ignore
24168          */
24169     simpleAccess: function(obj, subsc) {
24170         return obj[subsc];
24171     },
24172
24173         /**
24174          * @ignore
24175          */
24176     getJsonAccessor: function(){
24177         var re = /[\[\.]/;
24178         return function(expr) {
24179             try {
24180                 return(re.test(expr))
24181                     ? new Function("obj", "return obj." + expr)
24182                     : function(obj){
24183                         return obj[expr];
24184                     };
24185             } catch(e){}
24186             return Roo.emptyFn;
24187         };
24188     }(),
24189
24190     /**
24191      * Create a data block containing Roo.data.Records from an XML document.
24192      * @param {Object} o An object which contains an Array of row objects in the property specified
24193      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24194      * which contains the total size of the dataset.
24195      * @return {Object} data A data block which is used by an Roo.data.Store object as
24196      * a cache of Roo.data.Records.
24197      */
24198     readRecords : function(o){
24199         /**
24200          * After any data loads, the raw JSON data is available for further custom processing.
24201          * @type Object
24202          */
24203         this.o = o;
24204         var s = this.meta, Record = this.recordType,
24205             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24206
24207 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24208         if (!this.ef) {
24209             if(s.totalProperty) {
24210                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24211                 }
24212                 if(s.successProperty) {
24213                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24214                 }
24215                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24216                 if (s.id) {
24217                         var g = this.getJsonAccessor(s.id);
24218                         this.getId = function(rec) {
24219                                 var r = g(rec);  
24220                                 return (r === undefined || r === "") ? null : r;
24221                         };
24222                 } else {
24223                         this.getId = function(){return null;};
24224                 }
24225             this.ef = [];
24226             for(var jj = 0; jj < fl; jj++){
24227                 f = fi[jj];
24228                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24229                 this.ef[jj] = this.getJsonAccessor(map);
24230             }
24231         }
24232
24233         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24234         if(s.totalProperty){
24235             var vt = parseInt(this.getTotal(o), 10);
24236             if(!isNaN(vt)){
24237                 totalRecords = vt;
24238             }
24239         }
24240         if(s.successProperty){
24241             var vs = this.getSuccess(o);
24242             if(vs === false || vs === 'false'){
24243                 success = false;
24244             }
24245         }
24246         var records = [];
24247         for(var i = 0; i < c; i++){
24248                 var n = root[i];
24249             var values = {};
24250             var id = this.getId(n);
24251             for(var j = 0; j < fl; j++){
24252                 f = fi[j];
24253             var v = this.ef[j](n);
24254             if (!f.convert) {
24255                 Roo.log('missing convert for ' + f.name);
24256                 Roo.log(f);
24257                 continue;
24258             }
24259             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24260             }
24261             var record = new Record(values, id);
24262             record.json = n;
24263             records[i] = record;
24264         }
24265         return {
24266             raw : o,
24267             success : success,
24268             records : records,
24269             totalRecords : totalRecords
24270         };
24271     }
24272 });/*
24273  * Based on:
24274  * Ext JS Library 1.1.1
24275  * Copyright(c) 2006-2007, Ext JS, LLC.
24276  *
24277  * Originally Released Under LGPL - original licence link has changed is not relivant.
24278  *
24279  * Fork - LGPL
24280  * <script type="text/javascript">
24281  */
24282
24283 /**
24284  * @class Roo.data.XmlReader
24285  * @extends Roo.data.DataReader
24286  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24287  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24288  * <p>
24289  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24290  * header in the HTTP response must be set to "text/xml".</em>
24291  * <p>
24292  * Example code:
24293  * <pre><code>
24294 var RecordDef = Roo.data.Record.create([
24295    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24296    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24297 ]);
24298 var myReader = new Roo.data.XmlReader({
24299    totalRecords: "results", // The element which contains the total dataset size (optional)
24300    record: "row",           // The repeated element which contains row information
24301    id: "id"                 // The element within the row that provides an ID for the record (optional)
24302 }, RecordDef);
24303 </code></pre>
24304  * <p>
24305  * This would consume an XML file like this:
24306  * <pre><code>
24307 &lt;?xml?>
24308 &lt;dataset>
24309  &lt;results>2&lt;/results>
24310  &lt;row>
24311    &lt;id>1&lt;/id>
24312    &lt;name>Bill&lt;/name>
24313    &lt;occupation>Gardener&lt;/occupation>
24314  &lt;/row>
24315  &lt;row>
24316    &lt;id>2&lt;/id>
24317    &lt;name>Ben&lt;/name>
24318    &lt;occupation>Horticulturalist&lt;/occupation>
24319  &lt;/row>
24320 &lt;/dataset>
24321 </code></pre>
24322  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24323  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24324  * paged from the remote server.
24325  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24326  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24327  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24328  * a record identifier value.
24329  * @constructor
24330  * Create a new XmlReader
24331  * @param {Object} meta Metadata configuration options
24332  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24333  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24334  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24335  */
24336 Roo.data.XmlReader = function(meta, recordType){
24337     meta = meta || {};
24338     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24339 };
24340 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24341     /**
24342      * This method is only used by a DataProxy which has retrieved data from a remote server.
24343          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24344          * to contain a method called 'responseXML' that returns an XML document object.
24345      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24346      * a cache of Roo.data.Records.
24347      */
24348     read : function(response){
24349         var doc = response.responseXML;
24350         if(!doc) {
24351             throw {message: "XmlReader.read: XML Document not available"};
24352         }
24353         return this.readRecords(doc);
24354     },
24355
24356     /**
24357      * Create a data block containing Roo.data.Records from an XML document.
24358          * @param {Object} doc A parsed XML document.
24359      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24360      * a cache of Roo.data.Records.
24361      */
24362     readRecords : function(doc){
24363         /**
24364          * After any data loads/reads, the raw XML Document is available for further custom processing.
24365          * @type XMLDocument
24366          */
24367         this.xmlData = doc;
24368         var root = doc.documentElement || doc;
24369         var q = Roo.DomQuery;
24370         var recordType = this.recordType, fields = recordType.prototype.fields;
24371         var sid = this.meta.id;
24372         var totalRecords = 0, success = true;
24373         if(this.meta.totalRecords){
24374             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24375         }
24376         
24377         if(this.meta.success){
24378             var sv = q.selectValue(this.meta.success, root, true);
24379             success = sv !== false && sv !== 'false';
24380         }
24381         var records = [];
24382         var ns = q.select(this.meta.record, root);
24383         for(var i = 0, len = ns.length; i < len; i++) {
24384                 var n = ns[i];
24385                 var values = {};
24386                 var id = sid ? q.selectValue(sid, n) : undefined;
24387                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24388                     var f = fields.items[j];
24389                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24390                     v = f.convert(v);
24391                     values[f.name] = v;
24392                 }
24393                 var record = new recordType(values, id);
24394                 record.node = n;
24395                 records[records.length] = record;
24396             }
24397
24398             return {
24399                 success : success,
24400                 records : records,
24401                 totalRecords : totalRecords || records.length
24402             };
24403     }
24404 });/*
24405  * Based on:
24406  * Ext JS Library 1.1.1
24407  * Copyright(c) 2006-2007, Ext JS, LLC.
24408  *
24409  * Originally Released Under LGPL - original licence link has changed is not relivant.
24410  *
24411  * Fork - LGPL
24412  * <script type="text/javascript">
24413  */
24414
24415 /**
24416  * @class Roo.data.ArrayReader
24417  * @extends Roo.data.DataReader
24418  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24419  * Each element of that Array represents a row of data fields. The
24420  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24421  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24422  * <p>
24423  * Example code:.
24424  * <pre><code>
24425 var RecordDef = Roo.data.Record.create([
24426     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24427     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24428 ]);
24429 var myReader = new Roo.data.ArrayReader({
24430     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24431 }, RecordDef);
24432 </code></pre>
24433  * <p>
24434  * This would consume an Array like this:
24435  * <pre><code>
24436 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24437   </code></pre>
24438  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24439  * @constructor
24440  * Create a new JsonReader
24441  * @param {Object} meta Metadata configuration options.
24442  * @param {Object} recordType Either an Array of field definition objects
24443  * as specified to {@link Roo.data.Record#create},
24444  * or an {@link Roo.data.Record} object
24445  * created using {@link Roo.data.Record#create}.
24446  */
24447 Roo.data.ArrayReader = function(meta, recordType){
24448     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24449 };
24450
24451 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24452     /**
24453      * Create a data block containing Roo.data.Records from an XML document.
24454      * @param {Object} o An Array of row objects which represents the dataset.
24455      * @return {Object} data A data block which is used by an Roo.data.Store object as
24456      * a cache of Roo.data.Records.
24457      */
24458     readRecords : function(o){
24459         var sid = this.meta ? this.meta.id : null;
24460         var recordType = this.recordType, fields = recordType.prototype.fields;
24461         var records = [];
24462         var root = o;
24463             for(var i = 0; i < root.length; i++){
24464                     var n = root[i];
24465                 var values = {};
24466                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24467                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24468                 var f = fields.items[j];
24469                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24470                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24471                 v = f.convert(v);
24472                 values[f.name] = v;
24473             }
24474                 var record = new recordType(values, id);
24475                 record.json = n;
24476                 records[records.length] = record;
24477             }
24478             return {
24479                 records : records,
24480                 totalRecords : records.length
24481             };
24482     }
24483 });/*
24484  * Based on:
24485  * Ext JS Library 1.1.1
24486  * Copyright(c) 2006-2007, Ext JS, LLC.
24487  *
24488  * Originally Released Under LGPL - original licence link has changed is not relivant.
24489  *
24490  * Fork - LGPL
24491  * <script type="text/javascript">
24492  */
24493
24494
24495 /**
24496  * @class Roo.data.Tree
24497  * @extends Roo.util.Observable
24498  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24499  * in the tree have most standard DOM functionality.
24500  * @constructor
24501  * @param {Node} root (optional) The root node
24502  */
24503 Roo.data.Tree = function(root){
24504    this.nodeHash = {};
24505    /**
24506     * The root node for this tree
24507     * @type Node
24508     */
24509    this.root = null;
24510    if(root){
24511        this.setRootNode(root);
24512    }
24513    this.addEvents({
24514        /**
24515         * @event append
24516         * Fires when a new child node is appended to a node in this tree.
24517         * @param {Tree} tree The owner tree
24518         * @param {Node} parent The parent node
24519         * @param {Node} node The newly appended node
24520         * @param {Number} index The index of the newly appended node
24521         */
24522        "append" : true,
24523        /**
24524         * @event remove
24525         * Fires when a child node is removed from a node in this tree.
24526         * @param {Tree} tree The owner tree
24527         * @param {Node} parent The parent node
24528         * @param {Node} node The child node removed
24529         */
24530        "remove" : true,
24531        /**
24532         * @event move
24533         * Fires when a node is moved to a new location in the tree
24534         * @param {Tree} tree The owner tree
24535         * @param {Node} node The node moved
24536         * @param {Node} oldParent The old parent of this node
24537         * @param {Node} newParent The new parent of this node
24538         * @param {Number} index The index it was moved to
24539         */
24540        "move" : true,
24541        /**
24542         * @event insert
24543         * Fires when a new child node is inserted in a node in this tree.
24544         * @param {Tree} tree The owner tree
24545         * @param {Node} parent The parent node
24546         * @param {Node} node The child node inserted
24547         * @param {Node} refNode The child node the node was inserted before
24548         */
24549        "insert" : true,
24550        /**
24551         * @event beforeappend
24552         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24553         * @param {Tree} tree The owner tree
24554         * @param {Node} parent The parent node
24555         * @param {Node} node The child node to be appended
24556         */
24557        "beforeappend" : true,
24558        /**
24559         * @event beforeremove
24560         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24561         * @param {Tree} tree The owner tree
24562         * @param {Node} parent The parent node
24563         * @param {Node} node The child node to be removed
24564         */
24565        "beforeremove" : true,
24566        /**
24567         * @event beforemove
24568         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24569         * @param {Tree} tree The owner tree
24570         * @param {Node} node The node being moved
24571         * @param {Node} oldParent The parent of the node
24572         * @param {Node} newParent The new parent the node is moving to
24573         * @param {Number} index The index it is being moved to
24574         */
24575        "beforemove" : true,
24576        /**
24577         * @event beforeinsert
24578         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24579         * @param {Tree} tree The owner tree
24580         * @param {Node} parent The parent node
24581         * @param {Node} node The child node to be inserted
24582         * @param {Node} refNode The child node the node is being inserted before
24583         */
24584        "beforeinsert" : true
24585    });
24586
24587     Roo.data.Tree.superclass.constructor.call(this);
24588 };
24589
24590 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24591     pathSeparator: "/",
24592
24593     proxyNodeEvent : function(){
24594         return this.fireEvent.apply(this, arguments);
24595     },
24596
24597     /**
24598      * Returns the root node for this tree.
24599      * @return {Node}
24600      */
24601     getRootNode : function(){
24602         return this.root;
24603     },
24604
24605     /**
24606      * Sets the root node for this tree.
24607      * @param {Node} node
24608      * @return {Node}
24609      */
24610     setRootNode : function(node){
24611         this.root = node;
24612         node.ownerTree = this;
24613         node.isRoot = true;
24614         this.registerNode(node);
24615         return node;
24616     },
24617
24618     /**
24619      * Gets a node in this tree by its id.
24620      * @param {String} id
24621      * @return {Node}
24622      */
24623     getNodeById : function(id){
24624         return this.nodeHash[id];
24625     },
24626
24627     registerNode : function(node){
24628         this.nodeHash[node.id] = node;
24629     },
24630
24631     unregisterNode : function(node){
24632         delete this.nodeHash[node.id];
24633     },
24634
24635     toString : function(){
24636         return "[Tree"+(this.id?" "+this.id:"")+"]";
24637     }
24638 });
24639
24640 /**
24641  * @class Roo.data.Node
24642  * @extends Roo.util.Observable
24643  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24644  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24645  * @constructor
24646  * @param {Object} attributes The attributes/config for the node
24647  */
24648 Roo.data.Node = function(attributes){
24649     /**
24650      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24651      * @type {Object}
24652      */
24653     this.attributes = attributes || {};
24654     this.leaf = this.attributes.leaf;
24655     /**
24656      * The node id. @type String
24657      */
24658     this.id = this.attributes.id;
24659     if(!this.id){
24660         this.id = Roo.id(null, "ynode-");
24661         this.attributes.id = this.id;
24662     }
24663      
24664     
24665     /**
24666      * All child nodes of this node. @type Array
24667      */
24668     this.childNodes = [];
24669     if(!this.childNodes.indexOf){ // indexOf is a must
24670         this.childNodes.indexOf = function(o){
24671             for(var i = 0, len = this.length; i < len; i++){
24672                 if(this[i] == o) {
24673                     return i;
24674                 }
24675             }
24676             return -1;
24677         };
24678     }
24679     /**
24680      * The parent node for this node. @type Node
24681      */
24682     this.parentNode = null;
24683     /**
24684      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24685      */
24686     this.firstChild = null;
24687     /**
24688      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24689      */
24690     this.lastChild = null;
24691     /**
24692      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24693      */
24694     this.previousSibling = null;
24695     /**
24696      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24697      */
24698     this.nextSibling = null;
24699
24700     this.addEvents({
24701        /**
24702         * @event append
24703         * Fires when a new child node is appended
24704         * @param {Tree} tree The owner tree
24705         * @param {Node} this This node
24706         * @param {Node} node The newly appended node
24707         * @param {Number} index The index of the newly appended node
24708         */
24709        "append" : true,
24710        /**
24711         * @event remove
24712         * Fires when a child node is removed
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} this This node
24715         * @param {Node} node The removed node
24716         */
24717        "remove" : true,
24718        /**
24719         * @event move
24720         * Fires when this node is moved to a new location in the tree
24721         * @param {Tree} tree The owner tree
24722         * @param {Node} this This node
24723         * @param {Node} oldParent The old parent of this node
24724         * @param {Node} newParent The new parent of this node
24725         * @param {Number} index The index it was moved to
24726         */
24727        "move" : true,
24728        /**
24729         * @event insert
24730         * Fires when a new child node is inserted.
24731         * @param {Tree} tree The owner tree
24732         * @param {Node} this This node
24733         * @param {Node} node The child node inserted
24734         * @param {Node} refNode The child node the node was inserted before
24735         */
24736        "insert" : true,
24737        /**
24738         * @event beforeappend
24739         * Fires before a new child is appended, return false to cancel the append.
24740         * @param {Tree} tree The owner tree
24741         * @param {Node} this This node
24742         * @param {Node} node The child node to be appended
24743         */
24744        "beforeappend" : true,
24745        /**
24746         * @event beforeremove
24747         * Fires before a child is removed, return false to cancel the remove.
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} this This node
24750         * @param {Node} node The child node to be removed
24751         */
24752        "beforeremove" : true,
24753        /**
24754         * @event beforemove
24755         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24756         * @param {Tree} tree The owner tree
24757         * @param {Node} this This node
24758         * @param {Node} oldParent The parent of this node
24759         * @param {Node} newParent The new parent this node is moving to
24760         * @param {Number} index The index it is being moved to
24761         */
24762        "beforemove" : true,
24763        /**
24764         * @event beforeinsert
24765         * Fires before a new child is inserted, return false to cancel the insert.
24766         * @param {Tree} tree The owner tree
24767         * @param {Node} this This node
24768         * @param {Node} node The child node to be inserted
24769         * @param {Node} refNode The child node the node is being inserted before
24770         */
24771        "beforeinsert" : true
24772    });
24773     this.listeners = this.attributes.listeners;
24774     Roo.data.Node.superclass.constructor.call(this);
24775 };
24776
24777 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24778     fireEvent : function(evtName){
24779         // first do standard event for this node
24780         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24781             return false;
24782         }
24783         // then bubble it up to the tree if the event wasn't cancelled
24784         var ot = this.getOwnerTree();
24785         if(ot){
24786             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24787                 return false;
24788             }
24789         }
24790         return true;
24791     },
24792
24793     /**
24794      * Returns true if this node is a leaf
24795      * @return {Boolean}
24796      */
24797     isLeaf : function(){
24798         return this.leaf === true;
24799     },
24800
24801     // private
24802     setFirstChild : function(node){
24803         this.firstChild = node;
24804     },
24805
24806     //private
24807     setLastChild : function(node){
24808         this.lastChild = node;
24809     },
24810
24811
24812     /**
24813      * Returns true if this node is the last child of its parent
24814      * @return {Boolean}
24815      */
24816     isLast : function(){
24817        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24818     },
24819
24820     /**
24821      * Returns true if this node is the first child of its parent
24822      * @return {Boolean}
24823      */
24824     isFirst : function(){
24825        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24826     },
24827
24828     hasChildNodes : function(){
24829         return !this.isLeaf() && this.childNodes.length > 0;
24830     },
24831
24832     /**
24833      * Insert node(s) as the last child node of this node.
24834      * @param {Node/Array} node The node or Array of nodes to append
24835      * @return {Node} The appended node if single append, or null if an array was passed
24836      */
24837     appendChild : function(node){
24838         var multi = false;
24839         if(node instanceof Array){
24840             multi = node;
24841         }else if(arguments.length > 1){
24842             multi = arguments;
24843         }
24844         // if passed an array or multiple args do them one by one
24845         if(multi){
24846             for(var i = 0, len = multi.length; i < len; i++) {
24847                 this.appendChild(multi[i]);
24848             }
24849         }else{
24850             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24851                 return false;
24852             }
24853             var index = this.childNodes.length;
24854             var oldParent = node.parentNode;
24855             // it's a move, make sure we move it cleanly
24856             if(oldParent){
24857                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24858                     return false;
24859                 }
24860                 oldParent.removeChild(node);
24861             }
24862             index = this.childNodes.length;
24863             if(index == 0){
24864                 this.setFirstChild(node);
24865             }
24866             this.childNodes.push(node);
24867             node.parentNode = this;
24868             var ps = this.childNodes[index-1];
24869             if(ps){
24870                 node.previousSibling = ps;
24871                 ps.nextSibling = node;
24872             }else{
24873                 node.previousSibling = null;
24874             }
24875             node.nextSibling = null;
24876             this.setLastChild(node);
24877             node.setOwnerTree(this.getOwnerTree());
24878             this.fireEvent("append", this.ownerTree, this, node, index);
24879             if(oldParent){
24880                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24881             }
24882             return node;
24883         }
24884     },
24885
24886     /**
24887      * Removes a child node from this node.
24888      * @param {Node} node The node to remove
24889      * @return {Node} The removed node
24890      */
24891     removeChild : function(node){
24892         var index = this.childNodes.indexOf(node);
24893         if(index == -1){
24894             return false;
24895         }
24896         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24897             return false;
24898         }
24899
24900         // remove it from childNodes collection
24901         this.childNodes.splice(index, 1);
24902
24903         // update siblings
24904         if(node.previousSibling){
24905             node.previousSibling.nextSibling = node.nextSibling;
24906         }
24907         if(node.nextSibling){
24908             node.nextSibling.previousSibling = node.previousSibling;
24909         }
24910
24911         // update child refs
24912         if(this.firstChild == node){
24913             this.setFirstChild(node.nextSibling);
24914         }
24915         if(this.lastChild == node){
24916             this.setLastChild(node.previousSibling);
24917         }
24918
24919         node.setOwnerTree(null);
24920         // clear any references from the node
24921         node.parentNode = null;
24922         node.previousSibling = null;
24923         node.nextSibling = null;
24924         this.fireEvent("remove", this.ownerTree, this, node);
24925         return node;
24926     },
24927
24928     /**
24929      * Inserts the first node before the second node in this nodes childNodes collection.
24930      * @param {Node} node The node to insert
24931      * @param {Node} refNode The node to insert before (if null the node is appended)
24932      * @return {Node} The inserted node
24933      */
24934     insertBefore : function(node, refNode){
24935         if(!refNode){ // like standard Dom, refNode can be null for append
24936             return this.appendChild(node);
24937         }
24938         // nothing to do
24939         if(node == refNode){
24940             return false;
24941         }
24942
24943         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24944             return false;
24945         }
24946         var index = this.childNodes.indexOf(refNode);
24947         var oldParent = node.parentNode;
24948         var refIndex = index;
24949
24950         // when moving internally, indexes will change after remove
24951         if(oldParent == this && this.childNodes.indexOf(node) < index){
24952             refIndex--;
24953         }
24954
24955         // it's a move, make sure we move it cleanly
24956         if(oldParent){
24957             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24958                 return false;
24959             }
24960             oldParent.removeChild(node);
24961         }
24962         if(refIndex == 0){
24963             this.setFirstChild(node);
24964         }
24965         this.childNodes.splice(refIndex, 0, node);
24966         node.parentNode = this;
24967         var ps = this.childNodes[refIndex-1];
24968         if(ps){
24969             node.previousSibling = ps;
24970             ps.nextSibling = node;
24971         }else{
24972             node.previousSibling = null;
24973         }
24974         node.nextSibling = refNode;
24975         refNode.previousSibling = node;
24976         node.setOwnerTree(this.getOwnerTree());
24977         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24978         if(oldParent){
24979             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24980         }
24981         return node;
24982     },
24983
24984     /**
24985      * Returns the child node at the specified index.
24986      * @param {Number} index
24987      * @return {Node}
24988      */
24989     item : function(index){
24990         return this.childNodes[index];
24991     },
24992
24993     /**
24994      * Replaces one child node in this node with another.
24995      * @param {Node} newChild The replacement node
24996      * @param {Node} oldChild The node to replace
24997      * @return {Node} The replaced node
24998      */
24999     replaceChild : function(newChild, oldChild){
25000         this.insertBefore(newChild, oldChild);
25001         this.removeChild(oldChild);
25002         return oldChild;
25003     },
25004
25005     /**
25006      * Returns the index of a child node
25007      * @param {Node} node
25008      * @return {Number} The index of the node or -1 if it was not found
25009      */
25010     indexOf : function(child){
25011         return this.childNodes.indexOf(child);
25012     },
25013
25014     /**
25015      * Returns the tree this node is in.
25016      * @return {Tree}
25017      */
25018     getOwnerTree : function(){
25019         // if it doesn't have one, look for one
25020         if(!this.ownerTree){
25021             var p = this;
25022             while(p){
25023                 if(p.ownerTree){
25024                     this.ownerTree = p.ownerTree;
25025                     break;
25026                 }
25027                 p = p.parentNode;
25028             }
25029         }
25030         return this.ownerTree;
25031     },
25032
25033     /**
25034      * Returns depth of this node (the root node has a depth of 0)
25035      * @return {Number}
25036      */
25037     getDepth : function(){
25038         var depth = 0;
25039         var p = this;
25040         while(p.parentNode){
25041             ++depth;
25042             p = p.parentNode;
25043         }
25044         return depth;
25045     },
25046
25047     // private
25048     setOwnerTree : function(tree){
25049         // if it's move, we need to update everyone
25050         if(tree != this.ownerTree){
25051             if(this.ownerTree){
25052                 this.ownerTree.unregisterNode(this);
25053             }
25054             this.ownerTree = tree;
25055             var cs = this.childNodes;
25056             for(var i = 0, len = cs.length; i < len; i++) {
25057                 cs[i].setOwnerTree(tree);
25058             }
25059             if(tree){
25060                 tree.registerNode(this);
25061             }
25062         }
25063     },
25064
25065     /**
25066      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25067      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25068      * @return {String} The path
25069      */
25070     getPath : function(attr){
25071         attr = attr || "id";
25072         var p = this.parentNode;
25073         var b = [this.attributes[attr]];
25074         while(p){
25075             b.unshift(p.attributes[attr]);
25076             p = p.parentNode;
25077         }
25078         var sep = this.getOwnerTree().pathSeparator;
25079         return sep + b.join(sep);
25080     },
25081
25082     /**
25083      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25084      * function call will be the scope provided or the current node. The arguments to the function
25085      * will be the args provided or the current node. If the function returns false at any point,
25086      * the bubble is stopped.
25087      * @param {Function} fn The function to call
25088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25090      */
25091     bubble : function(fn, scope, args){
25092         var p = this;
25093         while(p){
25094             if(fn.call(scope || p, args || p) === false){
25095                 break;
25096             }
25097             p = p.parentNode;
25098         }
25099     },
25100
25101     /**
25102      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25103      * function call will be the scope provided or the current node. The arguments to the function
25104      * will be the args provided or the current node. If the function returns false at any point,
25105      * the cascade is stopped on that branch.
25106      * @param {Function} fn The function to call
25107      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25108      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25109      */
25110     cascade : function(fn, scope, args){
25111         if(fn.call(scope || this, args || this) !== false){
25112             var cs = this.childNodes;
25113             for(var i = 0, len = cs.length; i < len; i++) {
25114                 cs[i].cascade(fn, scope, args);
25115             }
25116         }
25117     },
25118
25119     /**
25120      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25121      * function call will be the scope provided or the current node. The arguments to the function
25122      * will be the args provided or the current node. If the function returns false at any point,
25123      * the iteration stops.
25124      * @param {Function} fn The function to call
25125      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25126      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25127      */
25128     eachChild : function(fn, scope, args){
25129         var cs = this.childNodes;
25130         for(var i = 0, len = cs.length; i < len; i++) {
25131                 if(fn.call(scope || this, args || cs[i]) === false){
25132                     break;
25133                 }
25134         }
25135     },
25136
25137     /**
25138      * Finds the first child that has the attribute with the specified value.
25139      * @param {String} attribute The attribute name
25140      * @param {Mixed} value The value to search for
25141      * @return {Node} The found child or null if none was found
25142      */
25143     findChild : function(attribute, value){
25144         var cs = this.childNodes;
25145         for(var i = 0, len = cs.length; i < len; i++) {
25146                 if(cs[i].attributes[attribute] == value){
25147                     return cs[i];
25148                 }
25149         }
25150         return null;
25151     },
25152
25153     /**
25154      * Finds the first child by a custom function. The child matches if the function passed
25155      * returns true.
25156      * @param {Function} fn
25157      * @param {Object} scope (optional)
25158      * @return {Node} The found child or null if none was found
25159      */
25160     findChildBy : function(fn, scope){
25161         var cs = this.childNodes;
25162         for(var i = 0, len = cs.length; i < len; i++) {
25163                 if(fn.call(scope||cs[i], cs[i]) === true){
25164                     return cs[i];
25165                 }
25166         }
25167         return null;
25168     },
25169
25170     /**
25171      * Sorts this nodes children using the supplied sort function
25172      * @param {Function} fn
25173      * @param {Object} scope (optional)
25174      */
25175     sort : function(fn, scope){
25176         var cs = this.childNodes;
25177         var len = cs.length;
25178         if(len > 0){
25179             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25180             cs.sort(sortFn);
25181             for(var i = 0; i < len; i++){
25182                 var n = cs[i];
25183                 n.previousSibling = cs[i-1];
25184                 n.nextSibling = cs[i+1];
25185                 if(i == 0){
25186                     this.setFirstChild(n);
25187                 }
25188                 if(i == len-1){
25189                     this.setLastChild(n);
25190                 }
25191             }
25192         }
25193     },
25194
25195     /**
25196      * Returns true if this node is an ancestor (at any point) of the passed node.
25197      * @param {Node} node
25198      * @return {Boolean}
25199      */
25200     contains : function(node){
25201         return node.isAncestor(this);
25202     },
25203
25204     /**
25205      * Returns true if the passed node is an ancestor (at any point) of this node.
25206      * @param {Node} node
25207      * @return {Boolean}
25208      */
25209     isAncestor : function(node){
25210         var p = this.parentNode;
25211         while(p){
25212             if(p == node){
25213                 return true;
25214             }
25215             p = p.parentNode;
25216         }
25217         return false;
25218     },
25219
25220     toString : function(){
25221         return "[Node"+(this.id?" "+this.id:"")+"]";
25222     }
25223 });/*
25224  * Based on:
25225  * Ext JS Library 1.1.1
25226  * Copyright(c) 2006-2007, Ext JS, LLC.
25227  *
25228  * Originally Released Under LGPL - original licence link has changed is not relivant.
25229  *
25230  * Fork - LGPL
25231  * <script type="text/javascript">
25232  */
25233  (function(){ 
25234 /**
25235  * @class Roo.Layer
25236  * @extends Roo.Element
25237  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25238  * automatic maintaining of shadow/shim positions.
25239  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25240  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25241  * you can pass a string with a CSS class name. False turns off the shadow.
25242  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25243  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25244  * @cfg {String} cls CSS class to add to the element
25245  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25246  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25247  * @constructor
25248  * @param {Object} config An object with config options.
25249  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25250  */
25251
25252 Roo.Layer = function(config, existingEl){
25253     config = config || {};
25254     var dh = Roo.DomHelper;
25255     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25256     if(existingEl){
25257         this.dom = Roo.getDom(existingEl);
25258     }
25259     if(!this.dom){
25260         var o = config.dh || {tag: "div", cls: "x-layer"};
25261         this.dom = dh.append(pel, o);
25262     }
25263     if(config.cls){
25264         this.addClass(config.cls);
25265     }
25266     this.constrain = config.constrain !== false;
25267     this.visibilityMode = Roo.Element.VISIBILITY;
25268     if(config.id){
25269         this.id = this.dom.id = config.id;
25270     }else{
25271         this.id = Roo.id(this.dom);
25272     }
25273     this.zindex = config.zindex || this.getZIndex();
25274     this.position("absolute", this.zindex);
25275     if(config.shadow){
25276         this.shadowOffset = config.shadowOffset || 4;
25277         this.shadow = new Roo.Shadow({
25278             offset : this.shadowOffset,
25279             mode : config.shadow
25280         });
25281     }else{
25282         this.shadowOffset = 0;
25283     }
25284     this.useShim = config.shim !== false && Roo.useShims;
25285     this.useDisplay = config.useDisplay;
25286     this.hide();
25287 };
25288
25289 var supr = Roo.Element.prototype;
25290
25291 // shims are shared among layer to keep from having 100 iframes
25292 var shims = [];
25293
25294 Roo.extend(Roo.Layer, Roo.Element, {
25295
25296     getZIndex : function(){
25297         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25298     },
25299
25300     getShim : function(){
25301         if(!this.useShim){
25302             return null;
25303         }
25304         if(this.shim){
25305             return this.shim;
25306         }
25307         var shim = shims.shift();
25308         if(!shim){
25309             shim = this.createShim();
25310             shim.enableDisplayMode('block');
25311             shim.dom.style.display = 'none';
25312             shim.dom.style.visibility = 'visible';
25313         }
25314         var pn = this.dom.parentNode;
25315         if(shim.dom.parentNode != pn){
25316             pn.insertBefore(shim.dom, this.dom);
25317         }
25318         shim.setStyle('z-index', this.getZIndex()-2);
25319         this.shim = shim;
25320         return shim;
25321     },
25322
25323     hideShim : function(){
25324         if(this.shim){
25325             this.shim.setDisplayed(false);
25326             shims.push(this.shim);
25327             delete this.shim;
25328         }
25329     },
25330
25331     disableShadow : function(){
25332         if(this.shadow){
25333             this.shadowDisabled = true;
25334             this.shadow.hide();
25335             this.lastShadowOffset = this.shadowOffset;
25336             this.shadowOffset = 0;
25337         }
25338     },
25339
25340     enableShadow : function(show){
25341         if(this.shadow){
25342             this.shadowDisabled = false;
25343             this.shadowOffset = this.lastShadowOffset;
25344             delete this.lastShadowOffset;
25345             if(show){
25346                 this.sync(true);
25347             }
25348         }
25349     },
25350
25351     // private
25352     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25353     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25354     sync : function(doShow){
25355         var sw = this.shadow;
25356         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25357             var sh = this.getShim();
25358
25359             var w = this.getWidth(),
25360                 h = this.getHeight();
25361
25362             var l = this.getLeft(true),
25363                 t = this.getTop(true);
25364
25365             if(sw && !this.shadowDisabled){
25366                 if(doShow && !sw.isVisible()){
25367                     sw.show(this);
25368                 }else{
25369                     sw.realign(l, t, w, h);
25370                 }
25371                 if(sh){
25372                     if(doShow){
25373                        sh.show();
25374                     }
25375                     // fit the shim behind the shadow, so it is shimmed too
25376                     var a = sw.adjusts, s = sh.dom.style;
25377                     s.left = (Math.min(l, l+a.l))+"px";
25378                     s.top = (Math.min(t, t+a.t))+"px";
25379                     s.width = (w+a.w)+"px";
25380                     s.height = (h+a.h)+"px";
25381                 }
25382             }else if(sh){
25383                 if(doShow){
25384                    sh.show();
25385                 }
25386                 sh.setSize(w, h);
25387                 sh.setLeftTop(l, t);
25388             }
25389             
25390         }
25391     },
25392
25393     // private
25394     destroy : function(){
25395         this.hideShim();
25396         if(this.shadow){
25397             this.shadow.hide();
25398         }
25399         this.removeAllListeners();
25400         var pn = this.dom.parentNode;
25401         if(pn){
25402             pn.removeChild(this.dom);
25403         }
25404         Roo.Element.uncache(this.id);
25405     },
25406
25407     remove : function(){
25408         this.destroy();
25409     },
25410
25411     // private
25412     beginUpdate : function(){
25413         this.updating = true;
25414     },
25415
25416     // private
25417     endUpdate : function(){
25418         this.updating = false;
25419         this.sync(true);
25420     },
25421
25422     // private
25423     hideUnders : function(negOffset){
25424         if(this.shadow){
25425             this.shadow.hide();
25426         }
25427         this.hideShim();
25428     },
25429
25430     // private
25431     constrainXY : function(){
25432         if(this.constrain){
25433             var vw = Roo.lib.Dom.getViewWidth(),
25434                 vh = Roo.lib.Dom.getViewHeight();
25435             var s = Roo.get(document).getScroll();
25436
25437             var xy = this.getXY();
25438             var x = xy[0], y = xy[1];   
25439             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25440             // only move it if it needs it
25441             var moved = false;
25442             // first validate right/bottom
25443             if((x + w) > vw+s.left){
25444                 x = vw - w - this.shadowOffset;
25445                 moved = true;
25446             }
25447             if((y + h) > vh+s.top){
25448                 y = vh - h - this.shadowOffset;
25449                 moved = true;
25450             }
25451             // then make sure top/left isn't negative
25452             if(x < s.left){
25453                 x = s.left;
25454                 moved = true;
25455             }
25456             if(y < s.top){
25457                 y = s.top;
25458                 moved = true;
25459             }
25460             if(moved){
25461                 if(this.avoidY){
25462                     var ay = this.avoidY;
25463                     if(y <= ay && (y+h) >= ay){
25464                         y = ay-h-5;   
25465                     }
25466                 }
25467                 xy = [x, y];
25468                 this.storeXY(xy);
25469                 supr.setXY.call(this, xy);
25470                 this.sync();
25471             }
25472         }
25473     },
25474
25475     isVisible : function(){
25476         return this.visible;    
25477     },
25478
25479     // private
25480     showAction : function(){
25481         this.visible = true; // track visibility to prevent getStyle calls
25482         if(this.useDisplay === true){
25483             this.setDisplayed("");
25484         }else if(this.lastXY){
25485             supr.setXY.call(this, this.lastXY);
25486         }else if(this.lastLT){
25487             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25488         }
25489     },
25490
25491     // private
25492     hideAction : function(){
25493         this.visible = false;
25494         if(this.useDisplay === true){
25495             this.setDisplayed(false);
25496         }else{
25497             this.setLeftTop(-10000,-10000);
25498         }
25499     },
25500
25501     // overridden Element method
25502     setVisible : function(v, a, d, c, e){
25503         if(v){
25504             this.showAction();
25505         }
25506         if(a && v){
25507             var cb = function(){
25508                 this.sync(true);
25509                 if(c){
25510                     c();
25511                 }
25512             }.createDelegate(this);
25513             supr.setVisible.call(this, true, true, d, cb, e);
25514         }else{
25515             if(!v){
25516                 this.hideUnders(true);
25517             }
25518             var cb = c;
25519             if(a){
25520                 cb = function(){
25521                     this.hideAction();
25522                     if(c){
25523                         c();
25524                     }
25525                 }.createDelegate(this);
25526             }
25527             supr.setVisible.call(this, v, a, d, cb, e);
25528             if(v){
25529                 this.sync(true);
25530             }else if(!a){
25531                 this.hideAction();
25532             }
25533         }
25534     },
25535
25536     storeXY : function(xy){
25537         delete this.lastLT;
25538         this.lastXY = xy;
25539     },
25540
25541     storeLeftTop : function(left, top){
25542         delete this.lastXY;
25543         this.lastLT = [left, top];
25544     },
25545
25546     // private
25547     beforeFx : function(){
25548         this.beforeAction();
25549         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25550     },
25551
25552     // private
25553     afterFx : function(){
25554         Roo.Layer.superclass.afterFx.apply(this, arguments);
25555         this.sync(this.isVisible());
25556     },
25557
25558     // private
25559     beforeAction : function(){
25560         if(!this.updating && this.shadow){
25561             this.shadow.hide();
25562         }
25563     },
25564
25565     // overridden Element method
25566     setLeft : function(left){
25567         this.storeLeftTop(left, this.getTop(true));
25568         supr.setLeft.apply(this, arguments);
25569         this.sync();
25570     },
25571
25572     setTop : function(top){
25573         this.storeLeftTop(this.getLeft(true), top);
25574         supr.setTop.apply(this, arguments);
25575         this.sync();
25576     },
25577
25578     setLeftTop : function(left, top){
25579         this.storeLeftTop(left, top);
25580         supr.setLeftTop.apply(this, arguments);
25581         this.sync();
25582     },
25583
25584     setXY : function(xy, a, d, c, e){
25585         this.fixDisplay();
25586         this.beforeAction();
25587         this.storeXY(xy);
25588         var cb = this.createCB(c);
25589         supr.setXY.call(this, xy, a, d, cb, e);
25590         if(!a){
25591             cb();
25592         }
25593     },
25594
25595     // private
25596     createCB : function(c){
25597         var el = this;
25598         return function(){
25599             el.constrainXY();
25600             el.sync(true);
25601             if(c){
25602                 c();
25603             }
25604         };
25605     },
25606
25607     // overridden Element method
25608     setX : function(x, a, d, c, e){
25609         this.setXY([x, this.getY()], a, d, c, e);
25610     },
25611
25612     // overridden Element method
25613     setY : function(y, a, d, c, e){
25614         this.setXY([this.getX(), y], a, d, c, e);
25615     },
25616
25617     // overridden Element method
25618     setSize : function(w, h, a, d, c, e){
25619         this.beforeAction();
25620         var cb = this.createCB(c);
25621         supr.setSize.call(this, w, h, a, d, cb, e);
25622         if(!a){
25623             cb();
25624         }
25625     },
25626
25627     // overridden Element method
25628     setWidth : function(w, a, d, c, e){
25629         this.beforeAction();
25630         var cb = this.createCB(c);
25631         supr.setWidth.call(this, w, a, d, cb, e);
25632         if(!a){
25633             cb();
25634         }
25635     },
25636
25637     // overridden Element method
25638     setHeight : function(h, a, d, c, e){
25639         this.beforeAction();
25640         var cb = this.createCB(c);
25641         supr.setHeight.call(this, h, a, d, cb, e);
25642         if(!a){
25643             cb();
25644         }
25645     },
25646
25647     // overridden Element method
25648     setBounds : function(x, y, w, h, a, d, c, e){
25649         this.beforeAction();
25650         var cb = this.createCB(c);
25651         if(!a){
25652             this.storeXY([x, y]);
25653             supr.setXY.call(this, [x, y]);
25654             supr.setSize.call(this, w, h, a, d, cb, e);
25655             cb();
25656         }else{
25657             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25658         }
25659         return this;
25660     },
25661     
25662     /**
25663      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25664      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25665      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25666      * @param {Number} zindex The new z-index to set
25667      * @return {this} The Layer
25668      */
25669     setZIndex : function(zindex){
25670         this.zindex = zindex;
25671         this.setStyle("z-index", zindex + 2);
25672         if(this.shadow){
25673             this.shadow.setZIndex(zindex + 1);
25674         }
25675         if(this.shim){
25676             this.shim.setStyle("z-index", zindex);
25677         }
25678     }
25679 });
25680 })();/*
25681  * Based on:
25682  * Ext JS Library 1.1.1
25683  * Copyright(c) 2006-2007, Ext JS, LLC.
25684  *
25685  * Originally Released Under LGPL - original licence link has changed is not relivant.
25686  *
25687  * Fork - LGPL
25688  * <script type="text/javascript">
25689  */
25690
25691
25692 /**
25693  * @class Roo.Shadow
25694  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25695  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25696  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25697  * @constructor
25698  * Create a new Shadow
25699  * @param {Object} config The config object
25700  */
25701 Roo.Shadow = function(config){
25702     Roo.apply(this, config);
25703     if(typeof this.mode != "string"){
25704         this.mode = this.defaultMode;
25705     }
25706     var o = this.offset, a = {h: 0};
25707     var rad = Math.floor(this.offset/2);
25708     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25709         case "drop":
25710             a.w = 0;
25711             a.l = a.t = o;
25712             a.t -= 1;
25713             if(Roo.isIE){
25714                 a.l -= this.offset + rad;
25715                 a.t -= this.offset + rad;
25716                 a.w -= rad;
25717                 a.h -= rad;
25718                 a.t += 1;
25719             }
25720         break;
25721         case "sides":
25722             a.w = (o*2);
25723             a.l = -o;
25724             a.t = o-1;
25725             if(Roo.isIE){
25726                 a.l -= (this.offset - rad);
25727                 a.t -= this.offset + rad;
25728                 a.l += 1;
25729                 a.w -= (this.offset - rad)*2;
25730                 a.w -= rad + 1;
25731                 a.h -= 1;
25732             }
25733         break;
25734         case "frame":
25735             a.w = a.h = (o*2);
25736             a.l = a.t = -o;
25737             a.t += 1;
25738             a.h -= 2;
25739             if(Roo.isIE){
25740                 a.l -= (this.offset - rad);
25741                 a.t -= (this.offset - rad);
25742                 a.l += 1;
25743                 a.w -= (this.offset + rad + 1);
25744                 a.h -= (this.offset + rad);
25745                 a.h += 1;
25746             }
25747         break;
25748     };
25749
25750     this.adjusts = a;
25751 };
25752
25753 Roo.Shadow.prototype = {
25754     /**
25755      * @cfg {String} mode
25756      * The shadow display mode.  Supports the following options:<br />
25757      * sides: Shadow displays on both sides and bottom only<br />
25758      * frame: Shadow displays equally on all four sides<br />
25759      * drop: Traditional bottom-right drop shadow (default)
25760      */
25761     /**
25762      * @cfg {String} offset
25763      * The number of pixels to offset the shadow from the element (defaults to 4)
25764      */
25765     offset: 4,
25766
25767     // private
25768     defaultMode: "drop",
25769
25770     /**
25771      * Displays the shadow under the target element
25772      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25773      */
25774     show : function(target){
25775         target = Roo.get(target);
25776         if(!this.el){
25777             this.el = Roo.Shadow.Pool.pull();
25778             if(this.el.dom.nextSibling != target.dom){
25779                 this.el.insertBefore(target);
25780             }
25781         }
25782         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25783         if(Roo.isIE){
25784             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25785         }
25786         this.realign(
25787             target.getLeft(true),
25788             target.getTop(true),
25789             target.getWidth(),
25790             target.getHeight()
25791         );
25792         this.el.dom.style.display = "block";
25793     },
25794
25795     /**
25796      * Returns true if the shadow is visible, else false
25797      */
25798     isVisible : function(){
25799         return this.el ? true : false;  
25800     },
25801
25802     /**
25803      * Direct alignment when values are already available. Show must be called at least once before
25804      * calling this method to ensure it is initialized.
25805      * @param {Number} left The target element left position
25806      * @param {Number} top The target element top position
25807      * @param {Number} width The target element width
25808      * @param {Number} height The target element height
25809      */
25810     realign : function(l, t, w, h){
25811         if(!this.el){
25812             return;
25813         }
25814         var a = this.adjusts, d = this.el.dom, s = d.style;
25815         var iea = 0;
25816         s.left = (l+a.l)+"px";
25817         s.top = (t+a.t)+"px";
25818         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25819  
25820         if(s.width != sws || s.height != shs){
25821             s.width = sws;
25822             s.height = shs;
25823             if(!Roo.isIE){
25824                 var cn = d.childNodes;
25825                 var sww = Math.max(0, (sw-12))+"px";
25826                 cn[0].childNodes[1].style.width = sww;
25827                 cn[1].childNodes[1].style.width = sww;
25828                 cn[2].childNodes[1].style.width = sww;
25829                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25830             }
25831         }
25832     },
25833
25834     /**
25835      * Hides this shadow
25836      */
25837     hide : function(){
25838         if(this.el){
25839             this.el.dom.style.display = "none";
25840             Roo.Shadow.Pool.push(this.el);
25841             delete this.el;
25842         }
25843     },
25844
25845     /**
25846      * Adjust the z-index of this shadow
25847      * @param {Number} zindex The new z-index
25848      */
25849     setZIndex : function(z){
25850         this.zIndex = z;
25851         if(this.el){
25852             this.el.setStyle("z-index", z);
25853         }
25854     }
25855 };
25856
25857 // Private utility class that manages the internal Shadow cache
25858 Roo.Shadow.Pool = function(){
25859     var p = [];
25860     var markup = Roo.isIE ?
25861                  '<div class="x-ie-shadow"></div>' :
25862                  '<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>';
25863     return {
25864         pull : function(){
25865             var sh = p.shift();
25866             if(!sh){
25867                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25868                 sh.autoBoxAdjust = false;
25869             }
25870             return sh;
25871         },
25872
25873         push : function(sh){
25874             p.push(sh);
25875         }
25876     };
25877 }();/*
25878  * Based on:
25879  * Ext JS Library 1.1.1
25880  * Copyright(c) 2006-2007, Ext JS, LLC.
25881  *
25882  * Originally Released Under LGPL - original licence link has changed is not relivant.
25883  *
25884  * Fork - LGPL
25885  * <script type="text/javascript">
25886  */
25887
25888
25889 /**
25890  * @class Roo.SplitBar
25891  * @extends Roo.util.Observable
25892  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25893  * <br><br>
25894  * Usage:
25895  * <pre><code>
25896 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25897                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25898 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25899 split.minSize = 100;
25900 split.maxSize = 600;
25901 split.animate = true;
25902 split.on('moved', splitterMoved);
25903 </code></pre>
25904  * @constructor
25905  * Create a new SplitBar
25906  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25907  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25908  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25909  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25910                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25911                         position of the SplitBar).
25912  */
25913 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25914     
25915     /** @private */
25916     this.el = Roo.get(dragElement, true);
25917     this.el.dom.unselectable = "on";
25918     /** @private */
25919     this.resizingEl = Roo.get(resizingElement, true);
25920
25921     /**
25922      * @private
25923      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25924      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25925      * @type Number
25926      */
25927     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25928     
25929     /**
25930      * The minimum size of the resizing element. (Defaults to 0)
25931      * @type Number
25932      */
25933     this.minSize = 0;
25934     
25935     /**
25936      * The maximum size of the resizing element. (Defaults to 2000)
25937      * @type Number
25938      */
25939     this.maxSize = 2000;
25940     
25941     /**
25942      * Whether to animate the transition to the new size
25943      * @type Boolean
25944      */
25945     this.animate = false;
25946     
25947     /**
25948      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25949      * @type Boolean
25950      */
25951     this.useShim = false;
25952     
25953     /** @private */
25954     this.shim = null;
25955     
25956     if(!existingProxy){
25957         /** @private */
25958         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25959     }else{
25960         this.proxy = Roo.get(existingProxy).dom;
25961     }
25962     /** @private */
25963     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25964     
25965     /** @private */
25966     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25967     
25968     /** @private */
25969     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25970     
25971     /** @private */
25972     this.dragSpecs = {};
25973     
25974     /**
25975      * @private The adapter to use to positon and resize elements
25976      */
25977     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25978     this.adapter.init(this);
25979     
25980     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25981         /** @private */
25982         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25983         this.el.addClass("x-splitbar-h");
25984     }else{
25985         /** @private */
25986         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25987         this.el.addClass("x-splitbar-v");
25988     }
25989     
25990     this.addEvents({
25991         /**
25992          * @event resize
25993          * Fires when the splitter is moved (alias for {@link #event-moved})
25994          * @param {Roo.SplitBar} this
25995          * @param {Number} newSize the new width or height
25996          */
25997         "resize" : true,
25998         /**
25999          * @event moved
26000          * Fires when the splitter is moved
26001          * @param {Roo.SplitBar} this
26002          * @param {Number} newSize the new width or height
26003          */
26004         "moved" : true,
26005         /**
26006          * @event beforeresize
26007          * Fires before the splitter is dragged
26008          * @param {Roo.SplitBar} this
26009          */
26010         "beforeresize" : true,
26011
26012         "beforeapply" : true
26013     });
26014
26015     Roo.util.Observable.call(this);
26016 };
26017
26018 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26019     onStartProxyDrag : function(x, y){
26020         this.fireEvent("beforeresize", this);
26021         if(!this.overlay){
26022             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26023             o.unselectable();
26024             o.enableDisplayMode("block");
26025             // all splitbars share the same overlay
26026             Roo.SplitBar.prototype.overlay = o;
26027         }
26028         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26029         this.overlay.show();
26030         Roo.get(this.proxy).setDisplayed("block");
26031         var size = this.adapter.getElementSize(this);
26032         this.activeMinSize = this.getMinimumSize();;
26033         this.activeMaxSize = this.getMaximumSize();;
26034         var c1 = size - this.activeMinSize;
26035         var c2 = Math.max(this.activeMaxSize - size, 0);
26036         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26037             this.dd.resetConstraints();
26038             this.dd.setXConstraint(
26039                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26040                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26041             );
26042             this.dd.setYConstraint(0, 0);
26043         }else{
26044             this.dd.resetConstraints();
26045             this.dd.setXConstraint(0, 0);
26046             this.dd.setYConstraint(
26047                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26048                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26049             );
26050          }
26051         this.dragSpecs.startSize = size;
26052         this.dragSpecs.startPoint = [x, y];
26053         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26054     },
26055     
26056     /** 
26057      * @private Called after the drag operation by the DDProxy
26058      */
26059     onEndProxyDrag : function(e){
26060         Roo.get(this.proxy).setDisplayed(false);
26061         var endPoint = Roo.lib.Event.getXY(e);
26062         if(this.overlay){
26063             this.overlay.hide();
26064         }
26065         var newSize;
26066         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26067             newSize = this.dragSpecs.startSize + 
26068                 (this.placement == Roo.SplitBar.LEFT ?
26069                     endPoint[0] - this.dragSpecs.startPoint[0] :
26070                     this.dragSpecs.startPoint[0] - endPoint[0]
26071                 );
26072         }else{
26073             newSize = this.dragSpecs.startSize + 
26074                 (this.placement == Roo.SplitBar.TOP ?
26075                     endPoint[1] - this.dragSpecs.startPoint[1] :
26076                     this.dragSpecs.startPoint[1] - endPoint[1]
26077                 );
26078         }
26079         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26080         if(newSize != this.dragSpecs.startSize){
26081             if(this.fireEvent('beforeapply', this, newSize) !== false){
26082                 this.adapter.setElementSize(this, newSize);
26083                 this.fireEvent("moved", this, newSize);
26084                 this.fireEvent("resize", this, newSize);
26085             }
26086         }
26087     },
26088     
26089     /**
26090      * Get the adapter this SplitBar uses
26091      * @return The adapter object
26092      */
26093     getAdapter : function(){
26094         return this.adapter;
26095     },
26096     
26097     /**
26098      * Set the adapter this SplitBar uses
26099      * @param {Object} adapter A SplitBar adapter object
26100      */
26101     setAdapter : function(adapter){
26102         this.adapter = adapter;
26103         this.adapter.init(this);
26104     },
26105     
26106     /**
26107      * Gets the minimum size for the resizing element
26108      * @return {Number} The minimum size
26109      */
26110     getMinimumSize : function(){
26111         return this.minSize;
26112     },
26113     
26114     /**
26115      * Sets the minimum size for the resizing element
26116      * @param {Number} minSize The minimum size
26117      */
26118     setMinimumSize : function(minSize){
26119         this.minSize = minSize;
26120     },
26121     
26122     /**
26123      * Gets the maximum size for the resizing element
26124      * @return {Number} The maximum size
26125      */
26126     getMaximumSize : function(){
26127         return this.maxSize;
26128     },
26129     
26130     /**
26131      * Sets the maximum size for the resizing element
26132      * @param {Number} maxSize The maximum size
26133      */
26134     setMaximumSize : function(maxSize){
26135         this.maxSize = maxSize;
26136     },
26137     
26138     /**
26139      * Sets the initialize size for the resizing element
26140      * @param {Number} size The initial size
26141      */
26142     setCurrentSize : function(size){
26143         var oldAnimate = this.animate;
26144         this.animate = false;
26145         this.adapter.setElementSize(this, size);
26146         this.animate = oldAnimate;
26147     },
26148     
26149     /**
26150      * Destroy this splitbar. 
26151      * @param {Boolean} removeEl True to remove the element
26152      */
26153     destroy : function(removeEl){
26154         if(this.shim){
26155             this.shim.remove();
26156         }
26157         this.dd.unreg();
26158         this.proxy.parentNode.removeChild(this.proxy);
26159         if(removeEl){
26160             this.el.remove();
26161         }
26162     }
26163 });
26164
26165 /**
26166  * @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.
26167  */
26168 Roo.SplitBar.createProxy = function(dir){
26169     var proxy = new Roo.Element(document.createElement("div"));
26170     proxy.unselectable();
26171     var cls = 'x-splitbar-proxy';
26172     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26173     document.body.appendChild(proxy.dom);
26174     return proxy.dom;
26175 };
26176
26177 /** 
26178  * @class Roo.SplitBar.BasicLayoutAdapter
26179  * Default Adapter. It assumes the splitter and resizing element are not positioned
26180  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26181  */
26182 Roo.SplitBar.BasicLayoutAdapter = function(){
26183 };
26184
26185 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26186     // do nothing for now
26187     init : function(s){
26188     
26189     },
26190     /**
26191      * Called before drag operations to get the current size of the resizing element. 
26192      * @param {Roo.SplitBar} s The SplitBar using this adapter
26193      */
26194      getElementSize : function(s){
26195         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26196             return s.resizingEl.getWidth();
26197         }else{
26198             return s.resizingEl.getHeight();
26199         }
26200     },
26201     
26202     /**
26203      * Called after drag operations to set the size of the resizing element.
26204      * @param {Roo.SplitBar} s The SplitBar using this adapter
26205      * @param {Number} newSize The new size to set
26206      * @param {Function} onComplete A function to be invoked when resizing is complete
26207      */
26208     setElementSize : function(s, newSize, onComplete){
26209         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26210             if(!s.animate){
26211                 s.resizingEl.setWidth(newSize);
26212                 if(onComplete){
26213                     onComplete(s, newSize);
26214                 }
26215             }else{
26216                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26217             }
26218         }else{
26219             
26220             if(!s.animate){
26221                 s.resizingEl.setHeight(newSize);
26222                 if(onComplete){
26223                     onComplete(s, newSize);
26224                 }
26225             }else{
26226                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26227             }
26228         }
26229     }
26230 };
26231
26232 /** 
26233  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26234  * @extends Roo.SplitBar.BasicLayoutAdapter
26235  * Adapter that  moves the splitter element to align with the resized sizing element. 
26236  * Used with an absolute positioned SplitBar.
26237  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26238  * document.body, make sure you assign an id to the body element.
26239  */
26240 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26241     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26242     this.container = Roo.get(container);
26243 };
26244
26245 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26246     init : function(s){
26247         this.basic.init(s);
26248     },
26249     
26250     getElementSize : function(s){
26251         return this.basic.getElementSize(s);
26252     },
26253     
26254     setElementSize : function(s, newSize, onComplete){
26255         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26256     },
26257     
26258     moveSplitter : function(s){
26259         var yes = Roo.SplitBar;
26260         switch(s.placement){
26261             case yes.LEFT:
26262                 s.el.setX(s.resizingEl.getRight());
26263                 break;
26264             case yes.RIGHT:
26265                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26266                 break;
26267             case yes.TOP:
26268                 s.el.setY(s.resizingEl.getBottom());
26269                 break;
26270             case yes.BOTTOM:
26271                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26272                 break;
26273         }
26274     }
26275 };
26276
26277 /**
26278  * Orientation constant - Create a vertical SplitBar
26279  * @static
26280  * @type Number
26281  */
26282 Roo.SplitBar.VERTICAL = 1;
26283
26284 /**
26285  * Orientation constant - Create a horizontal SplitBar
26286  * @static
26287  * @type Number
26288  */
26289 Roo.SplitBar.HORIZONTAL = 2;
26290
26291 /**
26292  * Placement constant - The resizing element is to the left of the splitter element
26293  * @static
26294  * @type Number
26295  */
26296 Roo.SplitBar.LEFT = 1;
26297
26298 /**
26299  * Placement constant - The resizing element is to the right of the splitter element
26300  * @static
26301  * @type Number
26302  */
26303 Roo.SplitBar.RIGHT = 2;
26304
26305 /**
26306  * Placement constant - The resizing element is positioned above the splitter element
26307  * @static
26308  * @type Number
26309  */
26310 Roo.SplitBar.TOP = 3;
26311
26312 /**
26313  * Placement constant - The resizing element is positioned under splitter element
26314  * @static
26315  * @type Number
26316  */
26317 Roo.SplitBar.BOTTOM = 4;
26318 /*
26319  * Based on:
26320  * Ext JS Library 1.1.1
26321  * Copyright(c) 2006-2007, Ext JS, LLC.
26322  *
26323  * Originally Released Under LGPL - original licence link has changed is not relivant.
26324  *
26325  * Fork - LGPL
26326  * <script type="text/javascript">
26327  */
26328
26329 /**
26330  * @class Roo.View
26331  * @extends Roo.util.Observable
26332  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26333  * This class also supports single and multi selection modes. <br>
26334  * Create a data model bound view:
26335  <pre><code>
26336  var store = new Roo.data.Store(...);
26337
26338  var view = new Roo.View({
26339     el : "my-element",
26340     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26341  
26342     singleSelect: true,
26343     selectedClass: "ydataview-selected",
26344     store: store
26345  });
26346
26347  // listen for node click?
26348  view.on("click", function(vw, index, node, e){
26349  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26350  });
26351
26352  // load XML data
26353  dataModel.load("foobar.xml");
26354  </code></pre>
26355  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26356  * <br><br>
26357  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26358  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26359  * 
26360  * Note: old style constructor is still suported (container, template, config)
26361  * 
26362  * @constructor
26363  * Create a new View
26364  * @param {Object} config The config object
26365  * 
26366  */
26367 Roo.View = function(config, depreciated_tpl, depreciated_config){
26368     
26369     this.parent = false;
26370     
26371     if (typeof(depreciated_tpl) == 'undefined') {
26372         // new way.. - universal constructor.
26373         Roo.apply(this, config);
26374         this.el  = Roo.get(this.el);
26375     } else {
26376         // old format..
26377         this.el  = Roo.get(config);
26378         this.tpl = depreciated_tpl;
26379         Roo.apply(this, depreciated_config);
26380     }
26381     this.wrapEl  = this.el.wrap().wrap();
26382     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26383     
26384     
26385     if(typeof(this.tpl) == "string"){
26386         this.tpl = new Roo.Template(this.tpl);
26387     } else {
26388         // support xtype ctors..
26389         this.tpl = new Roo.factory(this.tpl, Roo);
26390     }
26391     
26392     
26393     this.tpl.compile();
26394     
26395     /** @private */
26396     this.addEvents({
26397         /**
26398          * @event beforeclick
26399          * Fires before a click is processed. Returns false to cancel the default action.
26400          * @param {Roo.View} this
26401          * @param {Number} index The index of the target node
26402          * @param {HTMLElement} node The target node
26403          * @param {Roo.EventObject} e The raw event object
26404          */
26405             "beforeclick" : true,
26406         /**
26407          * @event click
26408          * Fires when a template node is clicked.
26409          * @param {Roo.View} this
26410          * @param {Number} index The index of the target node
26411          * @param {HTMLElement} node The target node
26412          * @param {Roo.EventObject} e The raw event object
26413          */
26414             "click" : true,
26415         /**
26416          * @event dblclick
26417          * Fires when a template node is double clicked.
26418          * @param {Roo.View} this
26419          * @param {Number} index The index of the target node
26420          * @param {HTMLElement} node The target node
26421          * @param {Roo.EventObject} e The raw event object
26422          */
26423             "dblclick" : true,
26424         /**
26425          * @event contextmenu
26426          * Fires when a template node is right clicked.
26427          * @param {Roo.View} this
26428          * @param {Number} index The index of the target node
26429          * @param {HTMLElement} node The target node
26430          * @param {Roo.EventObject} e The raw event object
26431          */
26432             "contextmenu" : true,
26433         /**
26434          * @event selectionchange
26435          * Fires when the selected nodes change.
26436          * @param {Roo.View} this
26437          * @param {Array} selections Array of the selected nodes
26438          */
26439             "selectionchange" : true,
26440     
26441         /**
26442          * @event beforeselect
26443          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26444          * @param {Roo.View} this
26445          * @param {HTMLElement} node The node to be selected
26446          * @param {Array} selections Array of currently selected nodes
26447          */
26448             "beforeselect" : true,
26449         /**
26450          * @event preparedata
26451          * Fires on every row to render, to allow you to change the data.
26452          * @param {Roo.View} this
26453          * @param {Object} data to be rendered (change this)
26454          */
26455           "preparedata" : true
26456           
26457           
26458         });
26459
26460
26461
26462     this.el.on({
26463         "click": this.onClick,
26464         "dblclick": this.onDblClick,
26465         "contextmenu": this.onContextMenu,
26466         scope:this
26467     });
26468
26469     this.selections = [];
26470     this.nodes = [];
26471     this.cmp = new Roo.CompositeElementLite([]);
26472     if(this.store){
26473         this.store = Roo.factory(this.store, Roo.data);
26474         this.setStore(this.store, true);
26475     }
26476     
26477     if ( this.footer && this.footer.xtype) {
26478            
26479          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26480         
26481         this.footer.dataSource = this.store;
26482         this.footer.container = fctr;
26483         this.footer = Roo.factory(this.footer, Roo);
26484         fctr.insertFirst(this.el);
26485         
26486         // this is a bit insane - as the paging toolbar seems to detach the el..
26487 //        dom.parentNode.parentNode.parentNode
26488          // they get detached?
26489     }
26490     
26491     
26492     Roo.View.superclass.constructor.call(this);
26493     
26494     
26495 };
26496
26497 Roo.extend(Roo.View, Roo.util.Observable, {
26498     
26499      /**
26500      * @cfg {Roo.data.Store} store Data store to load data from.
26501      */
26502     store : false,
26503     
26504     /**
26505      * @cfg {String|Roo.Element} el The container element.
26506      */
26507     el : '',
26508     
26509     /**
26510      * @cfg {String|Roo.Template} tpl The template used by this View 
26511      */
26512     tpl : false,
26513     /**
26514      * @cfg {String} dataName the named area of the template to use as the data area
26515      *                          Works with domtemplates roo-name="name"
26516      */
26517     dataName: false,
26518     /**
26519      * @cfg {String} selectedClass The css class to add to selected nodes
26520      */
26521     selectedClass : "x-view-selected",
26522      /**
26523      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26524      */
26525     emptyText : "",
26526     
26527     /**
26528      * @cfg {String} text to display on mask (default Loading)
26529      */
26530     mask : false,
26531     /**
26532      * @cfg {Boolean} multiSelect Allow multiple selection
26533      */
26534     multiSelect : false,
26535     /**
26536      * @cfg {Boolean} singleSelect Allow single selection
26537      */
26538     singleSelect:  false,
26539     
26540     /**
26541      * @cfg {Boolean} toggleSelect - selecting 
26542      */
26543     toggleSelect : false,
26544     
26545     /**
26546      * @cfg {Boolean} tickable - selecting 
26547      */
26548     tickable : false,
26549     
26550     /**
26551      * Returns the element this view is bound to.
26552      * @return {Roo.Element}
26553      */
26554     getEl : function(){
26555         return this.wrapEl;
26556     },
26557     
26558     
26559
26560     /**
26561      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26562      */
26563     refresh : function(){
26564         //Roo.log('refresh');
26565         var t = this.tpl;
26566         
26567         // if we are using something like 'domtemplate', then
26568         // the what gets used is:
26569         // t.applySubtemplate(NAME, data, wrapping data..)
26570         // the outer template then get' applied with
26571         //     the store 'extra data'
26572         // and the body get's added to the
26573         //      roo-name="data" node?
26574         //      <span class='roo-tpl-{name}'></span> ?????
26575         
26576         
26577         
26578         this.clearSelections();
26579         this.el.update("");
26580         var html = [];
26581         var records = this.store.getRange();
26582         if(records.length < 1) {
26583             
26584             // is this valid??  = should it render a template??
26585             
26586             this.el.update(this.emptyText);
26587             return;
26588         }
26589         var el = this.el;
26590         if (this.dataName) {
26591             this.el.update(t.apply(this.store.meta)); //????
26592             el = this.el.child('.roo-tpl-' + this.dataName);
26593         }
26594         
26595         for(var i = 0, len = records.length; i < len; i++){
26596             var data = this.prepareData(records[i].data, i, records[i]);
26597             this.fireEvent("preparedata", this, data, i, records[i]);
26598             
26599             var d = Roo.apply({}, data);
26600             
26601             if(this.tickable){
26602                 Roo.apply(d, {'roo-id' : Roo.id()});
26603                 
26604                 var _this = this;
26605             
26606                 Roo.each(this.parent.item, function(item){
26607                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26608                         return;
26609                     }
26610                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26611                 });
26612             }
26613             
26614             html[html.length] = Roo.util.Format.trim(
26615                 this.dataName ?
26616                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26617                     t.apply(d)
26618             );
26619         }
26620         
26621         
26622         
26623         el.update(html.join(""));
26624         this.nodes = el.dom.childNodes;
26625         this.updateIndexes(0);
26626     },
26627     
26628
26629     /**
26630      * Function to override to reformat the data that is sent to
26631      * the template for each node.
26632      * DEPRICATED - use the preparedata event handler.
26633      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26634      * a JSON object for an UpdateManager bound view).
26635      */
26636     prepareData : function(data, index, record)
26637     {
26638         this.fireEvent("preparedata", this, data, index, record);
26639         return data;
26640     },
26641
26642     onUpdate : function(ds, record){
26643         // Roo.log('on update');   
26644         this.clearSelections();
26645         var index = this.store.indexOf(record);
26646         var n = this.nodes[index];
26647         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26648         n.parentNode.removeChild(n);
26649         this.updateIndexes(index, index);
26650     },
26651
26652     
26653     
26654 // --------- FIXME     
26655     onAdd : function(ds, records, index)
26656     {
26657         //Roo.log(['on Add', ds, records, index] );        
26658         this.clearSelections();
26659         if(this.nodes.length == 0){
26660             this.refresh();
26661             return;
26662         }
26663         var n = this.nodes[index];
26664         for(var i = 0, len = records.length; i < len; i++){
26665             var d = this.prepareData(records[i].data, i, records[i]);
26666             if(n){
26667                 this.tpl.insertBefore(n, d);
26668             }else{
26669                 
26670                 this.tpl.append(this.el, d);
26671             }
26672         }
26673         this.updateIndexes(index);
26674     },
26675
26676     onRemove : function(ds, record, index){
26677        // Roo.log('onRemove');
26678         this.clearSelections();
26679         var el = this.dataName  ?
26680             this.el.child('.roo-tpl-' + this.dataName) :
26681             this.el; 
26682         
26683         el.dom.removeChild(this.nodes[index]);
26684         this.updateIndexes(index);
26685     },
26686
26687     /**
26688      * Refresh an individual node.
26689      * @param {Number} index
26690      */
26691     refreshNode : function(index){
26692         this.onUpdate(this.store, this.store.getAt(index));
26693     },
26694
26695     updateIndexes : function(startIndex, endIndex){
26696         var ns = this.nodes;
26697         startIndex = startIndex || 0;
26698         endIndex = endIndex || ns.length - 1;
26699         for(var i = startIndex; i <= endIndex; i++){
26700             ns[i].nodeIndex = i;
26701         }
26702     },
26703
26704     /**
26705      * Changes the data store this view uses and refresh the view.
26706      * @param {Store} store
26707      */
26708     setStore : function(store, initial){
26709         if(!initial && this.store){
26710             this.store.un("datachanged", this.refresh);
26711             this.store.un("add", this.onAdd);
26712             this.store.un("remove", this.onRemove);
26713             this.store.un("update", this.onUpdate);
26714             this.store.un("clear", this.refresh);
26715             this.store.un("beforeload", this.onBeforeLoad);
26716             this.store.un("load", this.onLoad);
26717             this.store.un("loadexception", this.onLoad);
26718         }
26719         if(store){
26720           
26721             store.on("datachanged", this.refresh, this);
26722             store.on("add", this.onAdd, this);
26723             store.on("remove", this.onRemove, this);
26724             store.on("update", this.onUpdate, this);
26725             store.on("clear", this.refresh, this);
26726             store.on("beforeload", this.onBeforeLoad, this);
26727             store.on("load", this.onLoad, this);
26728             store.on("loadexception", this.onLoad, this);
26729         }
26730         
26731         if(store){
26732             this.refresh();
26733         }
26734     },
26735     /**
26736      * onbeforeLoad - masks the loading area.
26737      *
26738      */
26739     onBeforeLoad : function(store,opts)
26740     {
26741          //Roo.log('onBeforeLoad');   
26742         if (!opts.add) {
26743             this.el.update("");
26744         }
26745         this.el.mask(this.mask ? this.mask : "Loading" ); 
26746     },
26747     onLoad : function ()
26748     {
26749         this.el.unmask();
26750     },
26751     
26752
26753     /**
26754      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26755      * @param {HTMLElement} node
26756      * @return {HTMLElement} The template node
26757      */
26758     findItemFromChild : function(node){
26759         var el = this.dataName  ?
26760             this.el.child('.roo-tpl-' + this.dataName,true) :
26761             this.el.dom; 
26762         
26763         if(!node || node.parentNode == el){
26764                     return node;
26765             }
26766             var p = node.parentNode;
26767             while(p && p != el){
26768             if(p.parentNode == el){
26769                 return p;
26770             }
26771             p = p.parentNode;
26772         }
26773             return null;
26774     },
26775
26776     /** @ignore */
26777     onClick : function(e){
26778         var item = this.findItemFromChild(e.getTarget());
26779         if(item){
26780             var index = this.indexOf(item);
26781             if(this.onItemClick(item, index, e) !== false){
26782                 this.fireEvent("click", this, index, item, e);
26783             }
26784         }else{
26785             this.clearSelections();
26786         }
26787     },
26788
26789     /** @ignore */
26790     onContextMenu : function(e){
26791         var item = this.findItemFromChild(e.getTarget());
26792         if(item){
26793             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26794         }
26795     },
26796
26797     /** @ignore */
26798     onDblClick : function(e){
26799         var item = this.findItemFromChild(e.getTarget());
26800         if(item){
26801             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26802         }
26803     },
26804
26805     onItemClick : function(item, index, e)
26806     {
26807         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26808             return false;
26809         }
26810         if (this.toggleSelect) {
26811             var m = this.isSelected(item) ? 'unselect' : 'select';
26812             //Roo.log(m);
26813             var _t = this;
26814             _t[m](item, true, false);
26815             return true;
26816         }
26817         if(this.multiSelect || this.singleSelect){
26818             if(this.multiSelect && e.shiftKey && this.lastSelection){
26819                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26820             }else{
26821                 this.select(item, this.multiSelect && e.ctrlKey);
26822                 this.lastSelection = item;
26823             }
26824             
26825             if(!this.tickable){
26826                 e.preventDefault();
26827             }
26828             
26829         }
26830         return true;
26831     },
26832
26833     /**
26834      * Get the number of selected nodes.
26835      * @return {Number}
26836      */
26837     getSelectionCount : function(){
26838         return this.selections.length;
26839     },
26840
26841     /**
26842      * Get the currently selected nodes.
26843      * @return {Array} An array of HTMLElements
26844      */
26845     getSelectedNodes : function(){
26846         return this.selections;
26847     },
26848
26849     /**
26850      * Get the indexes of the selected nodes.
26851      * @return {Array}
26852      */
26853     getSelectedIndexes : function(){
26854         var indexes = [], s = this.selections;
26855         for(var i = 0, len = s.length; i < len; i++){
26856             indexes.push(s[i].nodeIndex);
26857         }
26858         return indexes;
26859     },
26860
26861     /**
26862      * Clear all selections
26863      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26864      */
26865     clearSelections : function(suppressEvent){
26866         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26867             this.cmp.elements = this.selections;
26868             this.cmp.removeClass(this.selectedClass);
26869             this.selections = [];
26870             if(!suppressEvent){
26871                 this.fireEvent("selectionchange", this, this.selections);
26872             }
26873         }
26874     },
26875
26876     /**
26877      * Returns true if the passed node is selected
26878      * @param {HTMLElement/Number} node The node or node index
26879      * @return {Boolean}
26880      */
26881     isSelected : function(node){
26882         var s = this.selections;
26883         if(s.length < 1){
26884             return false;
26885         }
26886         node = this.getNode(node);
26887         return s.indexOf(node) !== -1;
26888     },
26889
26890     /**
26891      * Selects nodes.
26892      * @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
26893      * @param {Boolean} keepExisting (optional) true to keep existing selections
26894      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26895      */
26896     select : function(nodeInfo, keepExisting, suppressEvent){
26897         if(nodeInfo instanceof Array){
26898             if(!keepExisting){
26899                 this.clearSelections(true);
26900             }
26901             for(var i = 0, len = nodeInfo.length; i < len; i++){
26902                 this.select(nodeInfo[i], true, true);
26903             }
26904             return;
26905         } 
26906         var node = this.getNode(nodeInfo);
26907         if(!node || this.isSelected(node)){
26908             return; // already selected.
26909         }
26910         if(!keepExisting){
26911             this.clearSelections(true);
26912         }
26913         
26914         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26915             Roo.fly(node).addClass(this.selectedClass);
26916             this.selections.push(node);
26917             if(!suppressEvent){
26918                 this.fireEvent("selectionchange", this, this.selections);
26919             }
26920         }
26921         
26922         
26923     },
26924       /**
26925      * Unselects nodes.
26926      * @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
26927      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26928      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26929      */
26930     unselect : function(nodeInfo, keepExisting, suppressEvent)
26931     {
26932         if(nodeInfo instanceof Array){
26933             Roo.each(this.selections, function(s) {
26934                 this.unselect(s, nodeInfo);
26935             }, this);
26936             return;
26937         }
26938         var node = this.getNode(nodeInfo);
26939         if(!node || !this.isSelected(node)){
26940             //Roo.log("not selected");
26941             return; // not selected.
26942         }
26943         // fireevent???
26944         var ns = [];
26945         Roo.each(this.selections, function(s) {
26946             if (s == node ) {
26947                 Roo.fly(node).removeClass(this.selectedClass);
26948
26949                 return;
26950             }
26951             ns.push(s);
26952         },this);
26953         
26954         this.selections= ns;
26955         this.fireEvent("selectionchange", this, this.selections);
26956     },
26957
26958     /**
26959      * Gets a template node.
26960      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26961      * @return {HTMLElement} The node or null if it wasn't found
26962      */
26963     getNode : function(nodeInfo){
26964         if(typeof nodeInfo == "string"){
26965             return document.getElementById(nodeInfo);
26966         }else if(typeof nodeInfo == "number"){
26967             return this.nodes[nodeInfo];
26968         }
26969         return nodeInfo;
26970     },
26971
26972     /**
26973      * Gets a range template nodes.
26974      * @param {Number} startIndex
26975      * @param {Number} endIndex
26976      * @return {Array} An array of nodes
26977      */
26978     getNodes : function(start, end){
26979         var ns = this.nodes;
26980         start = start || 0;
26981         end = typeof end == "undefined" ? ns.length - 1 : end;
26982         var nodes = [];
26983         if(start <= end){
26984             for(var i = start; i <= end; i++){
26985                 nodes.push(ns[i]);
26986             }
26987         } else{
26988             for(var i = start; i >= end; i--){
26989                 nodes.push(ns[i]);
26990             }
26991         }
26992         return nodes;
26993     },
26994
26995     /**
26996      * Finds the index of the passed node
26997      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26998      * @return {Number} The index of the node or -1
26999      */
27000     indexOf : function(node){
27001         node = this.getNode(node);
27002         if(typeof node.nodeIndex == "number"){
27003             return node.nodeIndex;
27004         }
27005         var ns = this.nodes;
27006         for(var i = 0, len = ns.length; i < len; i++){
27007             if(ns[i] == node){
27008                 return i;
27009             }
27010         }
27011         return -1;
27012     }
27013 });
27014 /*
27015  * Based on:
27016  * Ext JS Library 1.1.1
27017  * Copyright(c) 2006-2007, Ext JS, LLC.
27018  *
27019  * Originally Released Under LGPL - original licence link has changed is not relivant.
27020  *
27021  * Fork - LGPL
27022  * <script type="text/javascript">
27023  */
27024
27025 /**
27026  * @class Roo.JsonView
27027  * @extends Roo.View
27028  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27029 <pre><code>
27030 var view = new Roo.JsonView({
27031     container: "my-element",
27032     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27033     multiSelect: true, 
27034     jsonRoot: "data" 
27035 });
27036
27037 // listen for node click?
27038 view.on("click", function(vw, index, node, e){
27039     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27040 });
27041
27042 // direct load of JSON data
27043 view.load("foobar.php");
27044
27045 // Example from my blog list
27046 var tpl = new Roo.Template(
27047     '&lt;div class="entry"&gt;' +
27048     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27049     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27050     "&lt;/div&gt;&lt;hr /&gt;"
27051 );
27052
27053 var moreView = new Roo.JsonView({
27054     container :  "entry-list", 
27055     template : tpl,
27056     jsonRoot: "posts"
27057 });
27058 moreView.on("beforerender", this.sortEntries, this);
27059 moreView.load({
27060     url: "/blog/get-posts.php",
27061     params: "allposts=true",
27062     text: "Loading Blog Entries..."
27063 });
27064 </code></pre>
27065
27066 * Note: old code is supported with arguments : (container, template, config)
27067
27068
27069  * @constructor
27070  * Create a new JsonView
27071  * 
27072  * @param {Object} config The config object
27073  * 
27074  */
27075 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27076     
27077     
27078     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27079
27080     var um = this.el.getUpdateManager();
27081     um.setRenderer(this);
27082     um.on("update", this.onLoad, this);
27083     um.on("failure", this.onLoadException, this);
27084
27085     /**
27086      * @event beforerender
27087      * Fires before rendering of the downloaded JSON data.
27088      * @param {Roo.JsonView} this
27089      * @param {Object} data The JSON data loaded
27090      */
27091     /**
27092      * @event load
27093      * Fires when data is loaded.
27094      * @param {Roo.JsonView} this
27095      * @param {Object} data The JSON data loaded
27096      * @param {Object} response The raw Connect response object
27097      */
27098     /**
27099      * @event loadexception
27100      * Fires when loading fails.
27101      * @param {Roo.JsonView} this
27102      * @param {Object} response The raw Connect response object
27103      */
27104     this.addEvents({
27105         'beforerender' : true,
27106         'load' : true,
27107         'loadexception' : true
27108     });
27109 };
27110 Roo.extend(Roo.JsonView, Roo.View, {
27111     /**
27112      * @type {String} The root property in the loaded JSON object that contains the data
27113      */
27114     jsonRoot : "",
27115
27116     /**
27117      * Refreshes the view.
27118      */
27119     refresh : function(){
27120         this.clearSelections();
27121         this.el.update("");
27122         var html = [];
27123         var o = this.jsonData;
27124         if(o && o.length > 0){
27125             for(var i = 0, len = o.length; i < len; i++){
27126                 var data = this.prepareData(o[i], i, o);
27127                 html[html.length] = this.tpl.apply(data);
27128             }
27129         }else{
27130             html.push(this.emptyText);
27131         }
27132         this.el.update(html.join(""));
27133         this.nodes = this.el.dom.childNodes;
27134         this.updateIndexes(0);
27135     },
27136
27137     /**
27138      * 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.
27139      * @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:
27140      <pre><code>
27141      view.load({
27142          url: "your-url.php",
27143          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27144          callback: yourFunction,
27145          scope: yourObject, //(optional scope)
27146          discardUrl: false,
27147          nocache: false,
27148          text: "Loading...",
27149          timeout: 30,
27150          scripts: false
27151      });
27152      </code></pre>
27153      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27154      * 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.
27155      * @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}
27156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27157      * @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.
27158      */
27159     load : function(){
27160         var um = this.el.getUpdateManager();
27161         um.update.apply(um, arguments);
27162     },
27163
27164     // note - render is a standard framework call...
27165     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27166     render : function(el, response){
27167         
27168         this.clearSelections();
27169         this.el.update("");
27170         var o;
27171         try{
27172             if (response != '') {
27173                 o = Roo.util.JSON.decode(response.responseText);
27174                 if(this.jsonRoot){
27175                     
27176                     o = o[this.jsonRoot];
27177                 }
27178             }
27179         } catch(e){
27180         }
27181         /**
27182          * The current JSON data or null
27183          */
27184         this.jsonData = o;
27185         this.beforeRender();
27186         this.refresh();
27187     },
27188
27189 /**
27190  * Get the number of records in the current JSON dataset
27191  * @return {Number}
27192  */
27193     getCount : function(){
27194         return this.jsonData ? this.jsonData.length : 0;
27195     },
27196
27197 /**
27198  * Returns the JSON object for the specified node(s)
27199  * @param {HTMLElement/Array} node The node or an array of nodes
27200  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27201  * you get the JSON object for the node
27202  */
27203     getNodeData : function(node){
27204         if(node instanceof Array){
27205             var data = [];
27206             for(var i = 0, len = node.length; i < len; i++){
27207                 data.push(this.getNodeData(node[i]));
27208             }
27209             return data;
27210         }
27211         return this.jsonData[this.indexOf(node)] || null;
27212     },
27213
27214     beforeRender : function(){
27215         this.snapshot = this.jsonData;
27216         if(this.sortInfo){
27217             this.sort.apply(this, this.sortInfo);
27218         }
27219         this.fireEvent("beforerender", this, this.jsonData);
27220     },
27221
27222     onLoad : function(el, o){
27223         this.fireEvent("load", this, this.jsonData, o);
27224     },
27225
27226     onLoadException : function(el, o){
27227         this.fireEvent("loadexception", this, o);
27228     },
27229
27230 /**
27231  * Filter the data by a specific property.
27232  * @param {String} property A property on your JSON objects
27233  * @param {String/RegExp} value Either string that the property values
27234  * should start with, or a RegExp to test against the property
27235  */
27236     filter : function(property, value){
27237         if(this.jsonData){
27238             var data = [];
27239             var ss = this.snapshot;
27240             if(typeof value == "string"){
27241                 var vlen = value.length;
27242                 if(vlen == 0){
27243                     this.clearFilter();
27244                     return;
27245                 }
27246                 value = value.toLowerCase();
27247                 for(var i = 0, len = ss.length; i < len; i++){
27248                     var o = ss[i];
27249                     if(o[property].substr(0, vlen).toLowerCase() == value){
27250                         data.push(o);
27251                     }
27252                 }
27253             } else if(value.exec){ // regex?
27254                 for(var i = 0, len = ss.length; i < len; i++){
27255                     var o = ss[i];
27256                     if(value.test(o[property])){
27257                         data.push(o);
27258                     }
27259                 }
27260             } else{
27261                 return;
27262             }
27263             this.jsonData = data;
27264             this.refresh();
27265         }
27266     },
27267
27268 /**
27269  * Filter by a function. The passed function will be called with each
27270  * object in the current dataset. If the function returns true the value is kept,
27271  * otherwise it is filtered.
27272  * @param {Function} fn
27273  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27274  */
27275     filterBy : function(fn, scope){
27276         if(this.jsonData){
27277             var data = [];
27278             var ss = this.snapshot;
27279             for(var i = 0, len = ss.length; i < len; i++){
27280                 var o = ss[i];
27281                 if(fn.call(scope || this, o)){
27282                     data.push(o);
27283                 }
27284             }
27285             this.jsonData = data;
27286             this.refresh();
27287         }
27288     },
27289
27290 /**
27291  * Clears the current filter.
27292  */
27293     clearFilter : function(){
27294         if(this.snapshot && this.jsonData != this.snapshot){
27295             this.jsonData = this.snapshot;
27296             this.refresh();
27297         }
27298     },
27299
27300
27301 /**
27302  * Sorts the data for this view and refreshes it.
27303  * @param {String} property A property on your JSON objects to sort on
27304  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27305  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27306  */
27307     sort : function(property, dir, sortType){
27308         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27309         if(this.jsonData){
27310             var p = property;
27311             var dsc = dir && dir.toLowerCase() == "desc";
27312             var f = function(o1, o2){
27313                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27314                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27315                 ;
27316                 if(v1 < v2){
27317                     return dsc ? +1 : -1;
27318                 } else if(v1 > v2){
27319                     return dsc ? -1 : +1;
27320                 } else{
27321                     return 0;
27322                 }
27323             };
27324             this.jsonData.sort(f);
27325             this.refresh();
27326             if(this.jsonData != this.snapshot){
27327                 this.snapshot.sort(f);
27328             }
27329         }
27330     }
27331 });/*
27332  * Based on:
27333  * Ext JS Library 1.1.1
27334  * Copyright(c) 2006-2007, Ext JS, LLC.
27335  *
27336  * Originally Released Under LGPL - original licence link has changed is not relivant.
27337  *
27338  * Fork - LGPL
27339  * <script type="text/javascript">
27340  */
27341  
27342
27343 /**
27344  * @class Roo.ColorPalette
27345  * @extends Roo.Component
27346  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27347  * Here's an example of typical usage:
27348  * <pre><code>
27349 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27350 cp.render('my-div');
27351
27352 cp.on('select', function(palette, selColor){
27353     // do something with selColor
27354 });
27355 </code></pre>
27356  * @constructor
27357  * Create a new ColorPalette
27358  * @param {Object} config The config object
27359  */
27360 Roo.ColorPalette = function(config){
27361     Roo.ColorPalette.superclass.constructor.call(this, config);
27362     this.addEvents({
27363         /**
27364              * @event select
27365              * Fires when a color is selected
27366              * @param {ColorPalette} this
27367              * @param {String} color The 6-digit color hex code (without the # symbol)
27368              */
27369         select: true
27370     });
27371
27372     if(this.handler){
27373         this.on("select", this.handler, this.scope, true);
27374     }
27375 };
27376 Roo.extend(Roo.ColorPalette, Roo.Component, {
27377     /**
27378      * @cfg {String} itemCls
27379      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27380      */
27381     itemCls : "x-color-palette",
27382     /**
27383      * @cfg {String} value
27384      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27385      * the hex codes are case-sensitive.
27386      */
27387     value : null,
27388     clickEvent:'click',
27389     // private
27390     ctype: "Roo.ColorPalette",
27391
27392     /**
27393      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27394      */
27395     allowReselect : false,
27396
27397     /**
27398      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27399      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27400      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27401      * of colors with the width setting until the box is symmetrical.</p>
27402      * <p>You can override individual colors if needed:</p>
27403      * <pre><code>
27404 var cp = new Roo.ColorPalette();
27405 cp.colors[0] = "FF0000";  // change the first box to red
27406 </code></pre>
27407
27408 Or you can provide a custom array of your own for complete control:
27409 <pre><code>
27410 var cp = new Roo.ColorPalette();
27411 cp.colors = ["000000", "993300", "333300"];
27412 </code></pre>
27413      * @type Array
27414      */
27415     colors : [
27416         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27417         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27418         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27419         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27420         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27421     ],
27422
27423     // private
27424     onRender : function(container, position){
27425         var t = new Roo.MasterTemplate(
27426             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27427         );
27428         var c = this.colors;
27429         for(var i = 0, len = c.length; i < len; i++){
27430             t.add([c[i]]);
27431         }
27432         var el = document.createElement("div");
27433         el.className = this.itemCls;
27434         t.overwrite(el);
27435         container.dom.insertBefore(el, position);
27436         this.el = Roo.get(el);
27437         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27438         if(this.clickEvent != 'click'){
27439             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27440         }
27441     },
27442
27443     // private
27444     afterRender : function(){
27445         Roo.ColorPalette.superclass.afterRender.call(this);
27446         if(this.value){
27447             var s = this.value;
27448             this.value = null;
27449             this.select(s);
27450         }
27451     },
27452
27453     // private
27454     handleClick : function(e, t){
27455         e.preventDefault();
27456         if(!this.disabled){
27457             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27458             this.select(c.toUpperCase());
27459         }
27460     },
27461
27462     /**
27463      * Selects the specified color in the palette (fires the select event)
27464      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27465      */
27466     select : function(color){
27467         color = color.replace("#", "");
27468         if(color != this.value || this.allowReselect){
27469             var el = this.el;
27470             if(this.value){
27471                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27472             }
27473             el.child("a.color-"+color).addClass("x-color-palette-sel");
27474             this.value = color;
27475             this.fireEvent("select", this, color);
27476         }
27477     }
27478 });/*
27479  * Based on:
27480  * Ext JS Library 1.1.1
27481  * Copyright(c) 2006-2007, Ext JS, LLC.
27482  *
27483  * Originally Released Under LGPL - original licence link has changed is not relivant.
27484  *
27485  * Fork - LGPL
27486  * <script type="text/javascript">
27487  */
27488  
27489 /**
27490  * @class Roo.DatePicker
27491  * @extends Roo.Component
27492  * Simple date picker class.
27493  * @constructor
27494  * Create a new DatePicker
27495  * @param {Object} config The config object
27496  */
27497 Roo.DatePicker = function(config){
27498     Roo.DatePicker.superclass.constructor.call(this, config);
27499
27500     this.value = config && config.value ?
27501                  config.value.clearTime() : new Date().clearTime();
27502
27503     this.addEvents({
27504         /**
27505              * @event select
27506              * Fires when a date is selected
27507              * @param {DatePicker} this
27508              * @param {Date} date The selected date
27509              */
27510         'select': true,
27511         /**
27512              * @event monthchange
27513              * Fires when the displayed month changes 
27514              * @param {DatePicker} this
27515              * @param {Date} date The selected month
27516              */
27517         'monthchange': true
27518     });
27519
27520     if(this.handler){
27521         this.on("select", this.handler,  this.scope || this);
27522     }
27523     // build the disabledDatesRE
27524     if(!this.disabledDatesRE && this.disabledDates){
27525         var dd = this.disabledDates;
27526         var re = "(?:";
27527         for(var i = 0; i < dd.length; i++){
27528             re += dd[i];
27529             if(i != dd.length-1) {
27530                 re += "|";
27531             }
27532         }
27533         this.disabledDatesRE = new RegExp(re + ")");
27534     }
27535 };
27536
27537 Roo.extend(Roo.DatePicker, Roo.Component, {
27538     /**
27539      * @cfg {String} todayText
27540      * The text to display on the button that selects the current date (defaults to "Today")
27541      */
27542     todayText : "Today",
27543     /**
27544      * @cfg {String} okText
27545      * The text to display on the ok button
27546      */
27547     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27548     /**
27549      * @cfg {String} cancelText
27550      * The text to display on the cancel button
27551      */
27552     cancelText : "Cancel",
27553     /**
27554      * @cfg {String} todayTip
27555      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27556      */
27557     todayTip : "{0} (Spacebar)",
27558     /**
27559      * @cfg {Date} minDate
27560      * Minimum allowable date (JavaScript date object, defaults to null)
27561      */
27562     minDate : null,
27563     /**
27564      * @cfg {Date} maxDate
27565      * Maximum allowable date (JavaScript date object, defaults to null)
27566      */
27567     maxDate : null,
27568     /**
27569      * @cfg {String} minText
27570      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27571      */
27572     minText : "This date is before the minimum date",
27573     /**
27574      * @cfg {String} maxText
27575      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27576      */
27577     maxText : "This date is after the maximum date",
27578     /**
27579      * @cfg {String} format
27580      * The default date format string which can be overriden for localization support.  The format must be
27581      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27582      */
27583     format : "m/d/y",
27584     /**
27585      * @cfg {Array} disabledDays
27586      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27587      */
27588     disabledDays : null,
27589     /**
27590      * @cfg {String} disabledDaysText
27591      * The tooltip to display when the date falls on a disabled day (defaults to "")
27592      */
27593     disabledDaysText : "",
27594     /**
27595      * @cfg {RegExp} disabledDatesRE
27596      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27597      */
27598     disabledDatesRE : null,
27599     /**
27600      * @cfg {String} disabledDatesText
27601      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27602      */
27603     disabledDatesText : "",
27604     /**
27605      * @cfg {Boolean} constrainToViewport
27606      * True to constrain the date picker to the viewport (defaults to true)
27607      */
27608     constrainToViewport : true,
27609     /**
27610      * @cfg {Array} monthNames
27611      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27612      */
27613     monthNames : Date.monthNames,
27614     /**
27615      * @cfg {Array} dayNames
27616      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27617      */
27618     dayNames : Date.dayNames,
27619     /**
27620      * @cfg {String} nextText
27621      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27622      */
27623     nextText: 'Next Month (Control+Right)',
27624     /**
27625      * @cfg {String} prevText
27626      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27627      */
27628     prevText: 'Previous Month (Control+Left)',
27629     /**
27630      * @cfg {String} monthYearText
27631      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27632      */
27633     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27634     /**
27635      * @cfg {Number} startDay
27636      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27637      */
27638     startDay : 0,
27639     /**
27640      * @cfg {Bool} showClear
27641      * Show a clear button (usefull for date form elements that can be blank.)
27642      */
27643     
27644     showClear: false,
27645     
27646     /**
27647      * Sets the value of the date field
27648      * @param {Date} value The date to set
27649      */
27650     setValue : function(value){
27651         var old = this.value;
27652         
27653         if (typeof(value) == 'string') {
27654          
27655             value = Date.parseDate(value, this.format);
27656         }
27657         if (!value) {
27658             value = new Date();
27659         }
27660         
27661         this.value = value.clearTime(true);
27662         if(this.el){
27663             this.update(this.value);
27664         }
27665     },
27666
27667     /**
27668      * Gets the current selected value of the date field
27669      * @return {Date} The selected date
27670      */
27671     getValue : function(){
27672         return this.value;
27673     },
27674
27675     // private
27676     focus : function(){
27677         if(this.el){
27678             this.update(this.activeDate);
27679         }
27680     },
27681
27682     // privateval
27683     onRender : function(container, position){
27684         
27685         var m = [
27686              '<table cellspacing="0">',
27687                 '<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>',
27688                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27689         var dn = this.dayNames;
27690         for(var i = 0; i < 7; i++){
27691             var d = this.startDay+i;
27692             if(d > 6){
27693                 d = d-7;
27694             }
27695             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27696         }
27697         m[m.length] = "</tr></thead><tbody><tr>";
27698         for(var i = 0; i < 42; i++) {
27699             if(i % 7 == 0 && i != 0){
27700                 m[m.length] = "</tr><tr>";
27701             }
27702             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27703         }
27704         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27705             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27706
27707         var el = document.createElement("div");
27708         el.className = "x-date-picker";
27709         el.innerHTML = m.join("");
27710
27711         container.dom.insertBefore(el, position);
27712
27713         this.el = Roo.get(el);
27714         this.eventEl = Roo.get(el.firstChild);
27715
27716         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27717             handler: this.showPrevMonth,
27718             scope: this,
27719             preventDefault:true,
27720             stopDefault:true
27721         });
27722
27723         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27724             handler: this.showNextMonth,
27725             scope: this,
27726             preventDefault:true,
27727             stopDefault:true
27728         });
27729
27730         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27731
27732         this.monthPicker = this.el.down('div.x-date-mp');
27733         this.monthPicker.enableDisplayMode('block');
27734         
27735         var kn = new Roo.KeyNav(this.eventEl, {
27736             "left" : function(e){
27737                 e.ctrlKey ?
27738                     this.showPrevMonth() :
27739                     this.update(this.activeDate.add("d", -1));
27740             },
27741
27742             "right" : function(e){
27743                 e.ctrlKey ?
27744                     this.showNextMonth() :
27745                     this.update(this.activeDate.add("d", 1));
27746             },
27747
27748             "up" : function(e){
27749                 e.ctrlKey ?
27750                     this.showNextYear() :
27751                     this.update(this.activeDate.add("d", -7));
27752             },
27753
27754             "down" : function(e){
27755                 e.ctrlKey ?
27756                     this.showPrevYear() :
27757                     this.update(this.activeDate.add("d", 7));
27758             },
27759
27760             "pageUp" : function(e){
27761                 this.showNextMonth();
27762             },
27763
27764             "pageDown" : function(e){
27765                 this.showPrevMonth();
27766             },
27767
27768             "enter" : function(e){
27769                 e.stopPropagation();
27770                 return true;
27771             },
27772
27773             scope : this
27774         });
27775
27776         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27777
27778         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27779
27780         this.el.unselectable();
27781         
27782         this.cells = this.el.select("table.x-date-inner tbody td");
27783         this.textNodes = this.el.query("table.x-date-inner tbody span");
27784
27785         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27786             text: "&#160;",
27787             tooltip: this.monthYearText
27788         });
27789
27790         this.mbtn.on('click', this.showMonthPicker, this);
27791         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27792
27793
27794         var today = (new Date()).dateFormat(this.format);
27795         
27796         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27797         if (this.showClear) {
27798             baseTb.add( new Roo.Toolbar.Fill());
27799         }
27800         baseTb.add({
27801             text: String.format(this.todayText, today),
27802             tooltip: String.format(this.todayTip, today),
27803             handler: this.selectToday,
27804             scope: this
27805         });
27806         
27807         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27808             
27809         //});
27810         if (this.showClear) {
27811             
27812             baseTb.add( new Roo.Toolbar.Fill());
27813             baseTb.add({
27814                 text: '&#160;',
27815                 cls: 'x-btn-icon x-btn-clear',
27816                 handler: function() {
27817                     //this.value = '';
27818                     this.fireEvent("select", this, '');
27819                 },
27820                 scope: this
27821             });
27822         }
27823         
27824         
27825         if(Roo.isIE){
27826             this.el.repaint();
27827         }
27828         this.update(this.value);
27829     },
27830
27831     createMonthPicker : function(){
27832         if(!this.monthPicker.dom.firstChild){
27833             var buf = ['<table border="0" cellspacing="0">'];
27834             for(var i = 0; i < 6; i++){
27835                 buf.push(
27836                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27837                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27838                     i == 0 ?
27839                     '<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>' :
27840                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27841                 );
27842             }
27843             buf.push(
27844                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27845                     this.okText,
27846                     '</button><button type="button" class="x-date-mp-cancel">',
27847                     this.cancelText,
27848                     '</button></td></tr>',
27849                 '</table>'
27850             );
27851             this.monthPicker.update(buf.join(''));
27852             this.monthPicker.on('click', this.onMonthClick, this);
27853             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27854
27855             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27856             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27857
27858             this.mpMonths.each(function(m, a, i){
27859                 i += 1;
27860                 if((i%2) == 0){
27861                     m.dom.xmonth = 5 + Math.round(i * .5);
27862                 }else{
27863                     m.dom.xmonth = Math.round((i-1) * .5);
27864                 }
27865             });
27866         }
27867     },
27868
27869     showMonthPicker : function(){
27870         this.createMonthPicker();
27871         var size = this.el.getSize();
27872         this.monthPicker.setSize(size);
27873         this.monthPicker.child('table').setSize(size);
27874
27875         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27876         this.updateMPMonth(this.mpSelMonth);
27877         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27878         this.updateMPYear(this.mpSelYear);
27879
27880         this.monthPicker.slideIn('t', {duration:.2});
27881     },
27882
27883     updateMPYear : function(y){
27884         this.mpyear = y;
27885         var ys = this.mpYears.elements;
27886         for(var i = 1; i <= 10; i++){
27887             var td = ys[i-1], y2;
27888             if((i%2) == 0){
27889                 y2 = y + Math.round(i * .5);
27890                 td.firstChild.innerHTML = y2;
27891                 td.xyear = y2;
27892             }else{
27893                 y2 = y - (5-Math.round(i * .5));
27894                 td.firstChild.innerHTML = y2;
27895                 td.xyear = y2;
27896             }
27897             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27898         }
27899     },
27900
27901     updateMPMonth : function(sm){
27902         this.mpMonths.each(function(m, a, i){
27903             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27904         });
27905     },
27906
27907     selectMPMonth: function(m){
27908         
27909     },
27910
27911     onMonthClick : function(e, t){
27912         e.stopEvent();
27913         var el = new Roo.Element(t), pn;
27914         if(el.is('button.x-date-mp-cancel')){
27915             this.hideMonthPicker();
27916         }
27917         else if(el.is('button.x-date-mp-ok')){
27918             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27919             this.hideMonthPicker();
27920         }
27921         else if(pn = el.up('td.x-date-mp-month', 2)){
27922             this.mpMonths.removeClass('x-date-mp-sel');
27923             pn.addClass('x-date-mp-sel');
27924             this.mpSelMonth = pn.dom.xmonth;
27925         }
27926         else if(pn = el.up('td.x-date-mp-year', 2)){
27927             this.mpYears.removeClass('x-date-mp-sel');
27928             pn.addClass('x-date-mp-sel');
27929             this.mpSelYear = pn.dom.xyear;
27930         }
27931         else if(el.is('a.x-date-mp-prev')){
27932             this.updateMPYear(this.mpyear-10);
27933         }
27934         else if(el.is('a.x-date-mp-next')){
27935             this.updateMPYear(this.mpyear+10);
27936         }
27937     },
27938
27939     onMonthDblClick : function(e, t){
27940         e.stopEvent();
27941         var el = new Roo.Element(t), pn;
27942         if(pn = el.up('td.x-date-mp-month', 2)){
27943             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27944             this.hideMonthPicker();
27945         }
27946         else if(pn = el.up('td.x-date-mp-year', 2)){
27947             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27948             this.hideMonthPicker();
27949         }
27950     },
27951
27952     hideMonthPicker : function(disableAnim){
27953         if(this.monthPicker){
27954             if(disableAnim === true){
27955                 this.monthPicker.hide();
27956             }else{
27957                 this.monthPicker.slideOut('t', {duration:.2});
27958             }
27959         }
27960     },
27961
27962     // private
27963     showPrevMonth : function(e){
27964         this.update(this.activeDate.add("mo", -1));
27965     },
27966
27967     // private
27968     showNextMonth : function(e){
27969         this.update(this.activeDate.add("mo", 1));
27970     },
27971
27972     // private
27973     showPrevYear : function(){
27974         this.update(this.activeDate.add("y", -1));
27975     },
27976
27977     // private
27978     showNextYear : function(){
27979         this.update(this.activeDate.add("y", 1));
27980     },
27981
27982     // private
27983     handleMouseWheel : function(e){
27984         var delta = e.getWheelDelta();
27985         if(delta > 0){
27986             this.showPrevMonth();
27987             e.stopEvent();
27988         } else if(delta < 0){
27989             this.showNextMonth();
27990             e.stopEvent();
27991         }
27992     },
27993
27994     // private
27995     handleDateClick : function(e, t){
27996         e.stopEvent();
27997         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27998             this.setValue(new Date(t.dateValue));
27999             this.fireEvent("select", this, this.value);
28000         }
28001     },
28002
28003     // private
28004     selectToday : function(){
28005         this.setValue(new Date().clearTime());
28006         this.fireEvent("select", this, this.value);
28007     },
28008
28009     // private
28010     update : function(date)
28011     {
28012         var vd = this.activeDate;
28013         this.activeDate = date;
28014         if(vd && this.el){
28015             var t = date.getTime();
28016             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28017                 this.cells.removeClass("x-date-selected");
28018                 this.cells.each(function(c){
28019                    if(c.dom.firstChild.dateValue == t){
28020                        c.addClass("x-date-selected");
28021                        setTimeout(function(){
28022                             try{c.dom.firstChild.focus();}catch(e){}
28023                        }, 50);
28024                        return false;
28025                    }
28026                 });
28027                 return;
28028             }
28029         }
28030         
28031         var days = date.getDaysInMonth();
28032         var firstOfMonth = date.getFirstDateOfMonth();
28033         var startingPos = firstOfMonth.getDay()-this.startDay;
28034
28035         if(startingPos <= this.startDay){
28036             startingPos += 7;
28037         }
28038
28039         var pm = date.add("mo", -1);
28040         var prevStart = pm.getDaysInMonth()-startingPos;
28041
28042         var cells = this.cells.elements;
28043         var textEls = this.textNodes;
28044         days += startingPos;
28045
28046         // convert everything to numbers so it's fast
28047         var day = 86400000;
28048         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28049         var today = new Date().clearTime().getTime();
28050         var sel = date.clearTime().getTime();
28051         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28052         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28053         var ddMatch = this.disabledDatesRE;
28054         var ddText = this.disabledDatesText;
28055         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28056         var ddaysText = this.disabledDaysText;
28057         var format = this.format;
28058
28059         var setCellClass = function(cal, cell){
28060             cell.title = "";
28061             var t = d.getTime();
28062             cell.firstChild.dateValue = t;
28063             if(t == today){
28064                 cell.className += " x-date-today";
28065                 cell.title = cal.todayText;
28066             }
28067             if(t == sel){
28068                 cell.className += " x-date-selected";
28069                 setTimeout(function(){
28070                     try{cell.firstChild.focus();}catch(e){}
28071                 }, 50);
28072             }
28073             // disabling
28074             if(t < min) {
28075                 cell.className = " x-date-disabled";
28076                 cell.title = cal.minText;
28077                 return;
28078             }
28079             if(t > max) {
28080                 cell.className = " x-date-disabled";
28081                 cell.title = cal.maxText;
28082                 return;
28083             }
28084             if(ddays){
28085                 if(ddays.indexOf(d.getDay()) != -1){
28086                     cell.title = ddaysText;
28087                     cell.className = " x-date-disabled";
28088                 }
28089             }
28090             if(ddMatch && format){
28091                 var fvalue = d.dateFormat(format);
28092                 if(ddMatch.test(fvalue)){
28093                     cell.title = ddText.replace("%0", fvalue);
28094                     cell.className = " x-date-disabled";
28095                 }
28096             }
28097         };
28098
28099         var i = 0;
28100         for(; i < startingPos; i++) {
28101             textEls[i].innerHTML = (++prevStart);
28102             d.setDate(d.getDate()+1);
28103             cells[i].className = "x-date-prevday";
28104             setCellClass(this, cells[i]);
28105         }
28106         for(; i < days; i++){
28107             intDay = i - startingPos + 1;
28108             textEls[i].innerHTML = (intDay);
28109             d.setDate(d.getDate()+1);
28110             cells[i].className = "x-date-active";
28111             setCellClass(this, cells[i]);
28112         }
28113         var extraDays = 0;
28114         for(; i < 42; i++) {
28115              textEls[i].innerHTML = (++extraDays);
28116              d.setDate(d.getDate()+1);
28117              cells[i].className = "x-date-nextday";
28118              setCellClass(this, cells[i]);
28119         }
28120
28121         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28122         this.fireEvent('monthchange', this, date);
28123         
28124         if(!this.internalRender){
28125             var main = this.el.dom.firstChild;
28126             var w = main.offsetWidth;
28127             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28128             Roo.fly(main).setWidth(w);
28129             this.internalRender = true;
28130             // opera does not respect the auto grow header center column
28131             // then, after it gets a width opera refuses to recalculate
28132             // without a second pass
28133             if(Roo.isOpera && !this.secondPass){
28134                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28135                 this.secondPass = true;
28136                 this.update.defer(10, this, [date]);
28137             }
28138         }
28139         
28140         
28141     }
28142 });        /*
28143  * Based on:
28144  * Ext JS Library 1.1.1
28145  * Copyright(c) 2006-2007, Ext JS, LLC.
28146  *
28147  * Originally Released Under LGPL - original licence link has changed is not relivant.
28148  *
28149  * Fork - LGPL
28150  * <script type="text/javascript">
28151  */
28152 /**
28153  * @class Roo.TabPanel
28154  * @extends Roo.util.Observable
28155  * A lightweight tab container.
28156  * <br><br>
28157  * Usage:
28158  * <pre><code>
28159 // basic tabs 1, built from existing content
28160 var tabs = new Roo.TabPanel("tabs1");
28161 tabs.addTab("script", "View Script");
28162 tabs.addTab("markup", "View Markup");
28163 tabs.activate("script");
28164
28165 // more advanced tabs, built from javascript
28166 var jtabs = new Roo.TabPanel("jtabs");
28167 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28168
28169 // set up the UpdateManager
28170 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28171 var updater = tab2.getUpdateManager();
28172 updater.setDefaultUrl("ajax1.htm");
28173 tab2.on('activate', updater.refresh, updater, true);
28174
28175 // Use setUrl for Ajax loading
28176 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28177 tab3.setUrl("ajax2.htm", null, true);
28178
28179 // Disabled tab
28180 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28181 tab4.disable();
28182
28183 jtabs.activate("jtabs-1");
28184  * </code></pre>
28185  * @constructor
28186  * Create a new TabPanel.
28187  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28188  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28189  */
28190 Roo.TabPanel = function(container, config){
28191     /**
28192     * The container element for this TabPanel.
28193     * @type Roo.Element
28194     */
28195     this.el = Roo.get(container, true);
28196     if(config){
28197         if(typeof config == "boolean"){
28198             this.tabPosition = config ? "bottom" : "top";
28199         }else{
28200             Roo.apply(this, config);
28201         }
28202     }
28203     if(this.tabPosition == "bottom"){
28204         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28205         this.el.addClass("x-tabs-bottom");
28206     }
28207     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28208     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28209     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28210     if(Roo.isIE){
28211         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28212     }
28213     if(this.tabPosition != "bottom"){
28214         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28215          * @type Roo.Element
28216          */
28217         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28218         this.el.addClass("x-tabs-top");
28219     }
28220     this.items = [];
28221
28222     this.bodyEl.setStyle("position", "relative");
28223
28224     this.active = null;
28225     this.activateDelegate = this.activate.createDelegate(this);
28226
28227     this.addEvents({
28228         /**
28229          * @event tabchange
28230          * Fires when the active tab changes
28231          * @param {Roo.TabPanel} this
28232          * @param {Roo.TabPanelItem} activePanel The new active tab
28233          */
28234         "tabchange": true,
28235         /**
28236          * @event beforetabchange
28237          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28238          * @param {Roo.TabPanel} this
28239          * @param {Object} e Set cancel to true on this object to cancel the tab change
28240          * @param {Roo.TabPanelItem} tab The tab being changed to
28241          */
28242         "beforetabchange" : true
28243     });
28244
28245     Roo.EventManager.onWindowResize(this.onResize, this);
28246     this.cpad = this.el.getPadding("lr");
28247     this.hiddenCount = 0;
28248
28249
28250     // toolbar on the tabbar support...
28251     if (this.toolbar) {
28252         var tcfg = this.toolbar;
28253         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28254         this.toolbar = new Roo.Toolbar(tcfg);
28255         if (Roo.isSafari) {
28256             var tbl = tcfg.container.child('table', true);
28257             tbl.setAttribute('width', '100%');
28258         }
28259         
28260     }
28261    
28262
28263
28264     Roo.TabPanel.superclass.constructor.call(this);
28265 };
28266
28267 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28268     /*
28269      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28270      */
28271     tabPosition : "top",
28272     /*
28273      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28274      */
28275     currentTabWidth : 0,
28276     /*
28277      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28278      */
28279     minTabWidth : 40,
28280     /*
28281      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28282      */
28283     maxTabWidth : 250,
28284     /*
28285      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28286      */
28287     preferredTabWidth : 175,
28288     /*
28289      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28290      */
28291     resizeTabs : false,
28292     /*
28293      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28294      */
28295     monitorResize : true,
28296     /*
28297      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28298      */
28299     toolbar : false,
28300
28301     /**
28302      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28303      * @param {String} id The id of the div to use <b>or create</b>
28304      * @param {String} text The text for the tab
28305      * @param {String} content (optional) Content to put in the TabPanelItem body
28306      * @param {Boolean} closable (optional) True to create a close icon on the tab
28307      * @return {Roo.TabPanelItem} The created TabPanelItem
28308      */
28309     addTab : function(id, text, content, closable){
28310         var item = new Roo.TabPanelItem(this, id, text, closable);
28311         this.addTabItem(item);
28312         if(content){
28313             item.setContent(content);
28314         }
28315         return item;
28316     },
28317
28318     /**
28319      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28320      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28321      * @return {Roo.TabPanelItem}
28322      */
28323     getTab : function(id){
28324         return this.items[id];
28325     },
28326
28327     /**
28328      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28329      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28330      */
28331     hideTab : function(id){
28332         var t = this.items[id];
28333         if(!t.isHidden()){
28334            t.setHidden(true);
28335            this.hiddenCount++;
28336            this.autoSizeTabs();
28337         }
28338     },
28339
28340     /**
28341      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28342      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28343      */
28344     unhideTab : function(id){
28345         var t = this.items[id];
28346         if(t.isHidden()){
28347            t.setHidden(false);
28348            this.hiddenCount--;
28349            this.autoSizeTabs();
28350         }
28351     },
28352
28353     /**
28354      * Adds an existing {@link Roo.TabPanelItem}.
28355      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28356      */
28357     addTabItem : function(item){
28358         this.items[item.id] = item;
28359         this.items.push(item);
28360         if(this.resizeTabs){
28361            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28362            this.autoSizeTabs();
28363         }else{
28364             item.autoSize();
28365         }
28366     },
28367
28368     /**
28369      * Removes a {@link Roo.TabPanelItem}.
28370      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28371      */
28372     removeTab : function(id){
28373         var items = this.items;
28374         var tab = items[id];
28375         if(!tab) { return; }
28376         var index = items.indexOf(tab);
28377         if(this.active == tab && items.length > 1){
28378             var newTab = this.getNextAvailable(index);
28379             if(newTab) {
28380                 newTab.activate();
28381             }
28382         }
28383         this.stripEl.dom.removeChild(tab.pnode.dom);
28384         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28385             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28386         }
28387         items.splice(index, 1);
28388         delete this.items[tab.id];
28389         tab.fireEvent("close", tab);
28390         tab.purgeListeners();
28391         this.autoSizeTabs();
28392     },
28393
28394     getNextAvailable : function(start){
28395         var items = this.items;
28396         var index = start;
28397         // look for a next tab that will slide over to
28398         // replace the one being removed
28399         while(index < items.length){
28400             var item = items[++index];
28401             if(item && !item.isHidden()){
28402                 return item;
28403             }
28404         }
28405         // if one isn't found select the previous tab (on the left)
28406         index = start;
28407         while(index >= 0){
28408             var item = items[--index];
28409             if(item && !item.isHidden()){
28410                 return item;
28411             }
28412         }
28413         return null;
28414     },
28415
28416     /**
28417      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28418      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28419      */
28420     disableTab : function(id){
28421         var tab = this.items[id];
28422         if(tab && this.active != tab){
28423             tab.disable();
28424         }
28425     },
28426
28427     /**
28428      * Enables a {@link Roo.TabPanelItem} that is disabled.
28429      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28430      */
28431     enableTab : function(id){
28432         var tab = this.items[id];
28433         tab.enable();
28434     },
28435
28436     /**
28437      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28438      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28439      * @return {Roo.TabPanelItem} The TabPanelItem.
28440      */
28441     activate : function(id){
28442         var tab = this.items[id];
28443         if(!tab){
28444             return null;
28445         }
28446         if(tab == this.active || tab.disabled){
28447             return tab;
28448         }
28449         var e = {};
28450         this.fireEvent("beforetabchange", this, e, tab);
28451         if(e.cancel !== true && !tab.disabled){
28452             if(this.active){
28453                 this.active.hide();
28454             }
28455             this.active = this.items[id];
28456             this.active.show();
28457             this.fireEvent("tabchange", this, this.active);
28458         }
28459         return tab;
28460     },
28461
28462     /**
28463      * Gets the active {@link Roo.TabPanelItem}.
28464      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28465      */
28466     getActiveTab : function(){
28467         return this.active;
28468     },
28469
28470     /**
28471      * Updates the tab body element to fit the height of the container element
28472      * for overflow scrolling
28473      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28474      */
28475     syncHeight : function(targetHeight){
28476         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28477         var bm = this.bodyEl.getMargins();
28478         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28479         this.bodyEl.setHeight(newHeight);
28480         return newHeight;
28481     },
28482
28483     onResize : function(){
28484         if(this.monitorResize){
28485             this.autoSizeTabs();
28486         }
28487     },
28488
28489     /**
28490      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28491      */
28492     beginUpdate : function(){
28493         this.updating = true;
28494     },
28495
28496     /**
28497      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28498      */
28499     endUpdate : function(){
28500         this.updating = false;
28501         this.autoSizeTabs();
28502     },
28503
28504     /**
28505      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28506      */
28507     autoSizeTabs : function(){
28508         var count = this.items.length;
28509         var vcount = count - this.hiddenCount;
28510         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28511             return;
28512         }
28513         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28514         var availWidth = Math.floor(w / vcount);
28515         var b = this.stripBody;
28516         if(b.getWidth() > w){
28517             var tabs = this.items;
28518             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28519             if(availWidth < this.minTabWidth){
28520                 /*if(!this.sleft){    // incomplete scrolling code
28521                     this.createScrollButtons();
28522                 }
28523                 this.showScroll();
28524                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28525             }
28526         }else{
28527             if(this.currentTabWidth < this.preferredTabWidth){
28528                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28529             }
28530         }
28531     },
28532
28533     /**
28534      * Returns the number of tabs in this TabPanel.
28535      * @return {Number}
28536      */
28537      getCount : function(){
28538          return this.items.length;
28539      },
28540
28541     /**
28542      * Resizes all the tabs to the passed width
28543      * @param {Number} The new width
28544      */
28545     setTabWidth : function(width){
28546         this.currentTabWidth = width;
28547         for(var i = 0, len = this.items.length; i < len; i++) {
28548                 if(!this.items[i].isHidden()) {
28549                 this.items[i].setWidth(width);
28550             }
28551         }
28552     },
28553
28554     /**
28555      * Destroys this TabPanel
28556      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28557      */
28558     destroy : function(removeEl){
28559         Roo.EventManager.removeResizeListener(this.onResize, this);
28560         for(var i = 0, len = this.items.length; i < len; i++){
28561             this.items[i].purgeListeners();
28562         }
28563         if(removeEl === true){
28564             this.el.update("");
28565             this.el.remove();
28566         }
28567     }
28568 });
28569
28570 /**
28571  * @class Roo.TabPanelItem
28572  * @extends Roo.util.Observable
28573  * Represents an individual item (tab plus body) in a TabPanel.
28574  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28575  * @param {String} id The id of this TabPanelItem
28576  * @param {String} text The text for the tab of this TabPanelItem
28577  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28578  */
28579 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28580     /**
28581      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28582      * @type Roo.TabPanel
28583      */
28584     this.tabPanel = tabPanel;
28585     /**
28586      * The id for this TabPanelItem
28587      * @type String
28588      */
28589     this.id = id;
28590     /** @private */
28591     this.disabled = false;
28592     /** @private */
28593     this.text = text;
28594     /** @private */
28595     this.loaded = false;
28596     this.closable = closable;
28597
28598     /**
28599      * The body element for this TabPanelItem.
28600      * @type Roo.Element
28601      */
28602     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28603     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28604     this.bodyEl.setStyle("display", "block");
28605     this.bodyEl.setStyle("zoom", "1");
28606     this.hideAction();
28607
28608     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28609     /** @private */
28610     this.el = Roo.get(els.el, true);
28611     this.inner = Roo.get(els.inner, true);
28612     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28613     this.pnode = Roo.get(els.el.parentNode, true);
28614     this.el.on("mousedown", this.onTabMouseDown, this);
28615     this.el.on("click", this.onTabClick, this);
28616     /** @private */
28617     if(closable){
28618         var c = Roo.get(els.close, true);
28619         c.dom.title = this.closeText;
28620         c.addClassOnOver("close-over");
28621         c.on("click", this.closeClick, this);
28622      }
28623
28624     this.addEvents({
28625          /**
28626          * @event activate
28627          * Fires when this tab becomes the active tab.
28628          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28629          * @param {Roo.TabPanelItem} this
28630          */
28631         "activate": true,
28632         /**
28633          * @event beforeclose
28634          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28635          * @param {Roo.TabPanelItem} this
28636          * @param {Object} e Set cancel to true on this object to cancel the close.
28637          */
28638         "beforeclose": true,
28639         /**
28640          * @event close
28641          * Fires when this tab is closed.
28642          * @param {Roo.TabPanelItem} this
28643          */
28644          "close": true,
28645         /**
28646          * @event deactivate
28647          * Fires when this tab is no longer the active tab.
28648          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28649          * @param {Roo.TabPanelItem} this
28650          */
28651          "deactivate" : true
28652     });
28653     this.hidden = false;
28654
28655     Roo.TabPanelItem.superclass.constructor.call(this);
28656 };
28657
28658 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28659     purgeListeners : function(){
28660        Roo.util.Observable.prototype.purgeListeners.call(this);
28661        this.el.removeAllListeners();
28662     },
28663     /**
28664      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28665      */
28666     show : function(){
28667         this.pnode.addClass("on");
28668         this.showAction();
28669         if(Roo.isOpera){
28670             this.tabPanel.stripWrap.repaint();
28671         }
28672         this.fireEvent("activate", this.tabPanel, this);
28673     },
28674
28675     /**
28676      * Returns true if this tab is the active tab.
28677      * @return {Boolean}
28678      */
28679     isActive : function(){
28680         return this.tabPanel.getActiveTab() == this;
28681     },
28682
28683     /**
28684      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28685      */
28686     hide : function(){
28687         this.pnode.removeClass("on");
28688         this.hideAction();
28689         this.fireEvent("deactivate", this.tabPanel, this);
28690     },
28691
28692     hideAction : function(){
28693         this.bodyEl.hide();
28694         this.bodyEl.setStyle("position", "absolute");
28695         this.bodyEl.setLeft("-20000px");
28696         this.bodyEl.setTop("-20000px");
28697     },
28698
28699     showAction : function(){
28700         this.bodyEl.setStyle("position", "relative");
28701         this.bodyEl.setTop("");
28702         this.bodyEl.setLeft("");
28703         this.bodyEl.show();
28704     },
28705
28706     /**
28707      * Set the tooltip for the tab.
28708      * @param {String} tooltip The tab's tooltip
28709      */
28710     setTooltip : function(text){
28711         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28712             this.textEl.dom.qtip = text;
28713             this.textEl.dom.removeAttribute('title');
28714         }else{
28715             this.textEl.dom.title = text;
28716         }
28717     },
28718
28719     onTabClick : function(e){
28720         e.preventDefault();
28721         this.tabPanel.activate(this.id);
28722     },
28723
28724     onTabMouseDown : function(e){
28725         e.preventDefault();
28726         this.tabPanel.activate(this.id);
28727     },
28728
28729     getWidth : function(){
28730         return this.inner.getWidth();
28731     },
28732
28733     setWidth : function(width){
28734         var iwidth = width - this.pnode.getPadding("lr");
28735         this.inner.setWidth(iwidth);
28736         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28737         this.pnode.setWidth(width);
28738     },
28739
28740     /**
28741      * Show or hide the tab
28742      * @param {Boolean} hidden True to hide or false to show.
28743      */
28744     setHidden : function(hidden){
28745         this.hidden = hidden;
28746         this.pnode.setStyle("display", hidden ? "none" : "");
28747     },
28748
28749     /**
28750      * Returns true if this tab is "hidden"
28751      * @return {Boolean}
28752      */
28753     isHidden : function(){
28754         return this.hidden;
28755     },
28756
28757     /**
28758      * Returns the text for this tab
28759      * @return {String}
28760      */
28761     getText : function(){
28762         return this.text;
28763     },
28764
28765     autoSize : function(){
28766         //this.el.beginMeasure();
28767         this.textEl.setWidth(1);
28768         /*
28769          *  #2804 [new] Tabs in Roojs
28770          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28771          */
28772         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28773         //this.el.endMeasure();
28774     },
28775
28776     /**
28777      * Sets the text for the tab (Note: this also sets the tooltip text)
28778      * @param {String} text The tab's text and tooltip
28779      */
28780     setText : function(text){
28781         this.text = text;
28782         this.textEl.update(text);
28783         this.setTooltip(text);
28784         if(!this.tabPanel.resizeTabs){
28785             this.autoSize();
28786         }
28787     },
28788     /**
28789      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28790      */
28791     activate : function(){
28792         this.tabPanel.activate(this.id);
28793     },
28794
28795     /**
28796      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28797      */
28798     disable : function(){
28799         if(this.tabPanel.active != this){
28800             this.disabled = true;
28801             this.pnode.addClass("disabled");
28802         }
28803     },
28804
28805     /**
28806      * Enables this TabPanelItem if it was previously disabled.
28807      */
28808     enable : function(){
28809         this.disabled = false;
28810         this.pnode.removeClass("disabled");
28811     },
28812
28813     /**
28814      * Sets the content for this TabPanelItem.
28815      * @param {String} content The content
28816      * @param {Boolean} loadScripts true to look for and load scripts
28817      */
28818     setContent : function(content, loadScripts){
28819         this.bodyEl.update(content, loadScripts);
28820     },
28821
28822     /**
28823      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28824      * @return {Roo.UpdateManager} The UpdateManager
28825      */
28826     getUpdateManager : function(){
28827         return this.bodyEl.getUpdateManager();
28828     },
28829
28830     /**
28831      * Set a URL to be used to load the content for this TabPanelItem.
28832      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28833      * @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)
28834      * @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)
28835      * @return {Roo.UpdateManager} The UpdateManager
28836      */
28837     setUrl : function(url, params, loadOnce){
28838         if(this.refreshDelegate){
28839             this.un('activate', this.refreshDelegate);
28840         }
28841         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28842         this.on("activate", this.refreshDelegate);
28843         return this.bodyEl.getUpdateManager();
28844     },
28845
28846     /** @private */
28847     _handleRefresh : function(url, params, loadOnce){
28848         if(!loadOnce || !this.loaded){
28849             var updater = this.bodyEl.getUpdateManager();
28850             updater.update(url, params, this._setLoaded.createDelegate(this));
28851         }
28852     },
28853
28854     /**
28855      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28856      *   Will fail silently if the setUrl method has not been called.
28857      *   This does not activate the panel, just updates its content.
28858      */
28859     refresh : function(){
28860         if(this.refreshDelegate){
28861            this.loaded = false;
28862            this.refreshDelegate();
28863         }
28864     },
28865
28866     /** @private */
28867     _setLoaded : function(){
28868         this.loaded = true;
28869     },
28870
28871     /** @private */
28872     closeClick : function(e){
28873         var o = {};
28874         e.stopEvent();
28875         this.fireEvent("beforeclose", this, o);
28876         if(o.cancel !== true){
28877             this.tabPanel.removeTab(this.id);
28878         }
28879     },
28880     /**
28881      * The text displayed in the tooltip for the close icon.
28882      * @type String
28883      */
28884     closeText : "Close this tab"
28885 });
28886
28887 /** @private */
28888 Roo.TabPanel.prototype.createStrip = function(container){
28889     var strip = document.createElement("div");
28890     strip.className = "x-tabs-wrap";
28891     container.appendChild(strip);
28892     return strip;
28893 };
28894 /** @private */
28895 Roo.TabPanel.prototype.createStripList = function(strip){
28896     // div wrapper for retard IE
28897     // returns the "tr" element.
28898     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28899         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28900         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28901     return strip.firstChild.firstChild.firstChild.firstChild;
28902 };
28903 /** @private */
28904 Roo.TabPanel.prototype.createBody = function(container){
28905     var body = document.createElement("div");
28906     Roo.id(body, "tab-body");
28907     Roo.fly(body).addClass("x-tabs-body");
28908     container.appendChild(body);
28909     return body;
28910 };
28911 /** @private */
28912 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28913     var body = Roo.getDom(id);
28914     if(!body){
28915         body = document.createElement("div");
28916         body.id = id;
28917     }
28918     Roo.fly(body).addClass("x-tabs-item-body");
28919     bodyEl.insertBefore(body, bodyEl.firstChild);
28920     return body;
28921 };
28922 /** @private */
28923 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28924     var td = document.createElement("td");
28925     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28926     //stripEl.appendChild(td);
28927     if(closable){
28928         td.className = "x-tabs-closable";
28929         if(!this.closeTpl){
28930             this.closeTpl = new Roo.Template(
28931                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28932                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28933                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28934             );
28935         }
28936         var el = this.closeTpl.overwrite(td, {"text": text});
28937         var close = el.getElementsByTagName("div")[0];
28938         var inner = el.getElementsByTagName("em")[0];
28939         return {"el": el, "close": close, "inner": inner};
28940     } else {
28941         if(!this.tabTpl){
28942             this.tabTpl = new Roo.Template(
28943                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28944                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28945             );
28946         }
28947         var el = this.tabTpl.overwrite(td, {"text": text});
28948         var inner = el.getElementsByTagName("em")[0];
28949         return {"el": el, "inner": inner};
28950     }
28951 };/*
28952  * Based on:
28953  * Ext JS Library 1.1.1
28954  * Copyright(c) 2006-2007, Ext JS, LLC.
28955  *
28956  * Originally Released Under LGPL - original licence link has changed is not relivant.
28957  *
28958  * Fork - LGPL
28959  * <script type="text/javascript">
28960  */
28961
28962 /**
28963  * @class Roo.Button
28964  * @extends Roo.util.Observable
28965  * Simple Button class
28966  * @cfg {String} text The button text
28967  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28968  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28969  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28970  * @cfg {Object} scope The scope of the handler
28971  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28972  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28973  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28974  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28975  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28976  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28977    applies if enableToggle = true)
28978  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28979  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28980   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28981  * @constructor
28982  * Create a new button
28983  * @param {Object} config The config object
28984  */
28985 Roo.Button = function(renderTo, config)
28986 {
28987     if (!config) {
28988         config = renderTo;
28989         renderTo = config.renderTo || false;
28990     }
28991     
28992     Roo.apply(this, config);
28993     this.addEvents({
28994         /**
28995              * @event click
28996              * Fires when this button is clicked
28997              * @param {Button} this
28998              * @param {EventObject} e The click event
28999              */
29000             "click" : true,
29001         /**
29002              * @event toggle
29003              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29004              * @param {Button} this
29005              * @param {Boolean} pressed
29006              */
29007             "toggle" : true,
29008         /**
29009              * @event mouseover
29010              * Fires when the mouse hovers over the button
29011              * @param {Button} this
29012              * @param {Event} e The event object
29013              */
29014         'mouseover' : true,
29015         /**
29016              * @event mouseout
29017              * Fires when the mouse exits the button
29018              * @param {Button} this
29019              * @param {Event} e The event object
29020              */
29021         'mouseout': true,
29022          /**
29023              * @event render
29024              * Fires when the button is rendered
29025              * @param {Button} this
29026              */
29027         'render': true
29028     });
29029     if(this.menu){
29030         this.menu = Roo.menu.MenuMgr.get(this.menu);
29031     }
29032     // register listeners first!!  - so render can be captured..
29033     Roo.util.Observable.call(this);
29034     if(renderTo){
29035         this.render(renderTo);
29036     }
29037     
29038   
29039 };
29040
29041 Roo.extend(Roo.Button, Roo.util.Observable, {
29042     /**
29043      * 
29044      */
29045     
29046     /**
29047      * Read-only. True if this button is hidden
29048      * @type Boolean
29049      */
29050     hidden : false,
29051     /**
29052      * Read-only. True if this button is disabled
29053      * @type Boolean
29054      */
29055     disabled : false,
29056     /**
29057      * Read-only. True if this button is pressed (only if enableToggle = true)
29058      * @type Boolean
29059      */
29060     pressed : false,
29061
29062     /**
29063      * @cfg {Number} tabIndex 
29064      * The DOM tabIndex for this button (defaults to undefined)
29065      */
29066     tabIndex : undefined,
29067
29068     /**
29069      * @cfg {Boolean} enableToggle
29070      * True to enable pressed/not pressed toggling (defaults to false)
29071      */
29072     enableToggle: false,
29073     /**
29074      * @cfg {Mixed} menu
29075      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29076      */
29077     menu : undefined,
29078     /**
29079      * @cfg {String} menuAlign
29080      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29081      */
29082     menuAlign : "tl-bl?",
29083
29084     /**
29085      * @cfg {String} iconCls
29086      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29087      */
29088     iconCls : undefined,
29089     /**
29090      * @cfg {String} type
29091      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29092      */
29093     type : 'button',
29094
29095     // private
29096     menuClassTarget: 'tr',
29097
29098     /**
29099      * @cfg {String} clickEvent
29100      * The type of event to map to the button's event handler (defaults to 'click')
29101      */
29102     clickEvent : 'click',
29103
29104     /**
29105      * @cfg {Boolean} handleMouseEvents
29106      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29107      */
29108     handleMouseEvents : true,
29109
29110     /**
29111      * @cfg {String} tooltipType
29112      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29113      */
29114     tooltipType : 'qtip',
29115
29116     /**
29117      * @cfg {String} cls
29118      * A CSS class to apply to the button's main element.
29119      */
29120     
29121     /**
29122      * @cfg {Roo.Template} template (Optional)
29123      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29124      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29125      * require code modifications if required elements (e.g. a button) aren't present.
29126      */
29127
29128     // private
29129     render : function(renderTo){
29130         var btn;
29131         if(this.hideParent){
29132             this.parentEl = Roo.get(renderTo);
29133         }
29134         if(!this.dhconfig){
29135             if(!this.template){
29136                 if(!Roo.Button.buttonTemplate){
29137                     // hideous table template
29138                     Roo.Button.buttonTemplate = new Roo.Template(
29139                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29140                         '<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>',
29141                         "</tr></tbody></table>");
29142                 }
29143                 this.template = Roo.Button.buttonTemplate;
29144             }
29145             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29146             var btnEl = btn.child("button:first");
29147             btnEl.on('focus', this.onFocus, this);
29148             btnEl.on('blur', this.onBlur, this);
29149             if(this.cls){
29150                 btn.addClass(this.cls);
29151             }
29152             if(this.icon){
29153                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29154             }
29155             if(this.iconCls){
29156                 btnEl.addClass(this.iconCls);
29157                 if(!this.cls){
29158                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29159                 }
29160             }
29161             if(this.tabIndex !== undefined){
29162                 btnEl.dom.tabIndex = this.tabIndex;
29163             }
29164             if(this.tooltip){
29165                 if(typeof this.tooltip == 'object'){
29166                     Roo.QuickTips.tips(Roo.apply({
29167                           target: btnEl.id
29168                     }, this.tooltip));
29169                 } else {
29170                     btnEl.dom[this.tooltipType] = this.tooltip;
29171                 }
29172             }
29173         }else{
29174             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29175         }
29176         this.el = btn;
29177         if(this.id){
29178             this.el.dom.id = this.el.id = this.id;
29179         }
29180         if(this.menu){
29181             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29182             this.menu.on("show", this.onMenuShow, this);
29183             this.menu.on("hide", this.onMenuHide, this);
29184         }
29185         btn.addClass("x-btn");
29186         if(Roo.isIE && !Roo.isIE7){
29187             this.autoWidth.defer(1, this);
29188         }else{
29189             this.autoWidth();
29190         }
29191         if(this.handleMouseEvents){
29192             btn.on("mouseover", this.onMouseOver, this);
29193             btn.on("mouseout", this.onMouseOut, this);
29194             btn.on("mousedown", this.onMouseDown, this);
29195         }
29196         btn.on(this.clickEvent, this.onClick, this);
29197         //btn.on("mouseup", this.onMouseUp, this);
29198         if(this.hidden){
29199             this.hide();
29200         }
29201         if(this.disabled){
29202             this.disable();
29203         }
29204         Roo.ButtonToggleMgr.register(this);
29205         if(this.pressed){
29206             this.el.addClass("x-btn-pressed");
29207         }
29208         if(this.repeat){
29209             var repeater = new Roo.util.ClickRepeater(btn,
29210                 typeof this.repeat == "object" ? this.repeat : {}
29211             );
29212             repeater.on("click", this.onClick,  this);
29213         }
29214         
29215         this.fireEvent('render', this);
29216         
29217     },
29218     /**
29219      * Returns the button's underlying element
29220      * @return {Roo.Element} The element
29221      */
29222     getEl : function(){
29223         return this.el;  
29224     },
29225     
29226     /**
29227      * Destroys this Button and removes any listeners.
29228      */
29229     destroy : function(){
29230         Roo.ButtonToggleMgr.unregister(this);
29231         this.el.removeAllListeners();
29232         this.purgeListeners();
29233         this.el.remove();
29234     },
29235
29236     // private
29237     autoWidth : function(){
29238         if(this.el){
29239             this.el.setWidth("auto");
29240             if(Roo.isIE7 && Roo.isStrict){
29241                 var ib = this.el.child('button');
29242                 if(ib && ib.getWidth() > 20){
29243                     ib.clip();
29244                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29245                 }
29246             }
29247             if(this.minWidth){
29248                 if(this.hidden){
29249                     this.el.beginMeasure();
29250                 }
29251                 if(this.el.getWidth() < this.minWidth){
29252                     this.el.setWidth(this.minWidth);
29253                 }
29254                 if(this.hidden){
29255                     this.el.endMeasure();
29256                 }
29257             }
29258         }
29259     },
29260
29261     /**
29262      * Assigns this button's click handler
29263      * @param {Function} handler The function to call when the button is clicked
29264      * @param {Object} scope (optional) Scope for the function passed in
29265      */
29266     setHandler : function(handler, scope){
29267         this.handler = handler;
29268         this.scope = scope;  
29269     },
29270     
29271     /**
29272      * Sets this button's text
29273      * @param {String} text The button text
29274      */
29275     setText : function(text){
29276         this.text = text;
29277         if(this.el){
29278             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29279         }
29280         this.autoWidth();
29281     },
29282     
29283     /**
29284      * Gets the text for this button
29285      * @return {String} The button text
29286      */
29287     getText : function(){
29288         return this.text;  
29289     },
29290     
29291     /**
29292      * Show this button
29293      */
29294     show: function(){
29295         this.hidden = false;
29296         if(this.el){
29297             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29298         }
29299     },
29300     
29301     /**
29302      * Hide this button
29303      */
29304     hide: function(){
29305         this.hidden = true;
29306         if(this.el){
29307             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29308         }
29309     },
29310     
29311     /**
29312      * Convenience function for boolean show/hide
29313      * @param {Boolean} visible True to show, false to hide
29314      */
29315     setVisible: function(visible){
29316         if(visible) {
29317             this.show();
29318         }else{
29319             this.hide();
29320         }
29321     },
29322     
29323     /**
29324      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29325      * @param {Boolean} state (optional) Force a particular state
29326      */
29327     toggle : function(state){
29328         state = state === undefined ? !this.pressed : state;
29329         if(state != this.pressed){
29330             if(state){
29331                 this.el.addClass("x-btn-pressed");
29332                 this.pressed = true;
29333                 this.fireEvent("toggle", this, true);
29334             }else{
29335                 this.el.removeClass("x-btn-pressed");
29336                 this.pressed = false;
29337                 this.fireEvent("toggle", this, false);
29338             }
29339             if(this.toggleHandler){
29340                 this.toggleHandler.call(this.scope || this, this, state);
29341             }
29342         }
29343     },
29344     
29345     /**
29346      * Focus the button
29347      */
29348     focus : function(){
29349         this.el.child('button:first').focus();
29350     },
29351     
29352     /**
29353      * Disable this button
29354      */
29355     disable : function(){
29356         if(this.el){
29357             this.el.addClass("x-btn-disabled");
29358         }
29359         this.disabled = true;
29360     },
29361     
29362     /**
29363      * Enable this button
29364      */
29365     enable : function(){
29366         if(this.el){
29367             this.el.removeClass("x-btn-disabled");
29368         }
29369         this.disabled = false;
29370     },
29371
29372     /**
29373      * Convenience function for boolean enable/disable
29374      * @param {Boolean} enabled True to enable, false to disable
29375      */
29376     setDisabled : function(v){
29377         this[v !== true ? "enable" : "disable"]();
29378     },
29379
29380     // private
29381     onClick : function(e)
29382     {
29383         if(e){
29384             e.preventDefault();
29385         }
29386         if(e.button != 0){
29387             return;
29388         }
29389         if(!this.disabled){
29390             if(this.enableToggle){
29391                 this.toggle();
29392             }
29393             if(this.menu && !this.menu.isVisible()){
29394                 this.menu.show(this.el, this.menuAlign);
29395             }
29396             this.fireEvent("click", this, e);
29397             if(this.handler){
29398                 this.el.removeClass("x-btn-over");
29399                 this.handler.call(this.scope || this, this, e);
29400             }
29401         }
29402     },
29403     // private
29404     onMouseOver : function(e){
29405         if(!this.disabled){
29406             this.el.addClass("x-btn-over");
29407             this.fireEvent('mouseover', this, e);
29408         }
29409     },
29410     // private
29411     onMouseOut : function(e){
29412         if(!e.within(this.el,  true)){
29413             this.el.removeClass("x-btn-over");
29414             this.fireEvent('mouseout', this, e);
29415         }
29416     },
29417     // private
29418     onFocus : function(e){
29419         if(!this.disabled){
29420             this.el.addClass("x-btn-focus");
29421         }
29422     },
29423     // private
29424     onBlur : function(e){
29425         this.el.removeClass("x-btn-focus");
29426     },
29427     // private
29428     onMouseDown : function(e){
29429         if(!this.disabled && e.button == 0){
29430             this.el.addClass("x-btn-click");
29431             Roo.get(document).on('mouseup', this.onMouseUp, this);
29432         }
29433     },
29434     // private
29435     onMouseUp : function(e){
29436         if(e.button == 0){
29437             this.el.removeClass("x-btn-click");
29438             Roo.get(document).un('mouseup', this.onMouseUp, this);
29439         }
29440     },
29441     // private
29442     onMenuShow : function(e){
29443         this.el.addClass("x-btn-menu-active");
29444     },
29445     // private
29446     onMenuHide : function(e){
29447         this.el.removeClass("x-btn-menu-active");
29448     }   
29449 });
29450
29451 // Private utility class used by Button
29452 Roo.ButtonToggleMgr = function(){
29453    var groups = {};
29454    
29455    function toggleGroup(btn, state){
29456        if(state){
29457            var g = groups[btn.toggleGroup];
29458            for(var i = 0, l = g.length; i < l; i++){
29459                if(g[i] != btn){
29460                    g[i].toggle(false);
29461                }
29462            }
29463        }
29464    }
29465    
29466    return {
29467        register : function(btn){
29468            if(!btn.toggleGroup){
29469                return;
29470            }
29471            var g = groups[btn.toggleGroup];
29472            if(!g){
29473                g = groups[btn.toggleGroup] = [];
29474            }
29475            g.push(btn);
29476            btn.on("toggle", toggleGroup);
29477        },
29478        
29479        unregister : function(btn){
29480            if(!btn.toggleGroup){
29481                return;
29482            }
29483            var g = groups[btn.toggleGroup];
29484            if(g){
29485                g.remove(btn);
29486                btn.un("toggle", toggleGroup);
29487            }
29488        }
29489    };
29490 }();/*
29491  * Based on:
29492  * Ext JS Library 1.1.1
29493  * Copyright(c) 2006-2007, Ext JS, LLC.
29494  *
29495  * Originally Released Under LGPL - original licence link has changed is not relivant.
29496  *
29497  * Fork - LGPL
29498  * <script type="text/javascript">
29499  */
29500  
29501 /**
29502  * @class Roo.SplitButton
29503  * @extends Roo.Button
29504  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29505  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29506  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29507  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29508  * @cfg {String} arrowTooltip The title attribute of the arrow
29509  * @constructor
29510  * Create a new menu button
29511  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29512  * @param {Object} config The config object
29513  */
29514 Roo.SplitButton = function(renderTo, config){
29515     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29516     /**
29517      * @event arrowclick
29518      * Fires when this button's arrow is clicked
29519      * @param {SplitButton} this
29520      * @param {EventObject} e The click event
29521      */
29522     this.addEvents({"arrowclick":true});
29523 };
29524
29525 Roo.extend(Roo.SplitButton, Roo.Button, {
29526     render : function(renderTo){
29527         // this is one sweet looking template!
29528         var tpl = new Roo.Template(
29529             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29530             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29531             '<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>',
29532             "</tbody></table></td><td>",
29533             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29534             '<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>',
29535             "</tbody></table></td></tr></table>"
29536         );
29537         var btn = tpl.append(renderTo, [this.text, this.type], true);
29538         var btnEl = btn.child("button");
29539         if(this.cls){
29540             btn.addClass(this.cls);
29541         }
29542         if(this.icon){
29543             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29544         }
29545         if(this.iconCls){
29546             btnEl.addClass(this.iconCls);
29547             if(!this.cls){
29548                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29549             }
29550         }
29551         this.el = btn;
29552         if(this.handleMouseEvents){
29553             btn.on("mouseover", this.onMouseOver, this);
29554             btn.on("mouseout", this.onMouseOut, this);
29555             btn.on("mousedown", this.onMouseDown, this);
29556             btn.on("mouseup", this.onMouseUp, this);
29557         }
29558         btn.on(this.clickEvent, this.onClick, this);
29559         if(this.tooltip){
29560             if(typeof this.tooltip == 'object'){
29561                 Roo.QuickTips.tips(Roo.apply({
29562                       target: btnEl.id
29563                 }, this.tooltip));
29564             } else {
29565                 btnEl.dom[this.tooltipType] = this.tooltip;
29566             }
29567         }
29568         if(this.arrowTooltip){
29569             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29570         }
29571         if(this.hidden){
29572             this.hide();
29573         }
29574         if(this.disabled){
29575             this.disable();
29576         }
29577         if(this.pressed){
29578             this.el.addClass("x-btn-pressed");
29579         }
29580         if(Roo.isIE && !Roo.isIE7){
29581             this.autoWidth.defer(1, this);
29582         }else{
29583             this.autoWidth();
29584         }
29585         if(this.menu){
29586             this.menu.on("show", this.onMenuShow, this);
29587             this.menu.on("hide", this.onMenuHide, this);
29588         }
29589         this.fireEvent('render', this);
29590     },
29591
29592     // private
29593     autoWidth : function(){
29594         if(this.el){
29595             var tbl = this.el.child("table:first");
29596             var tbl2 = this.el.child("table:last");
29597             this.el.setWidth("auto");
29598             tbl.setWidth("auto");
29599             if(Roo.isIE7 && Roo.isStrict){
29600                 var ib = this.el.child('button:first');
29601                 if(ib && ib.getWidth() > 20){
29602                     ib.clip();
29603                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29604                 }
29605             }
29606             if(this.minWidth){
29607                 if(this.hidden){
29608                     this.el.beginMeasure();
29609                 }
29610                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29611                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29612                 }
29613                 if(this.hidden){
29614                     this.el.endMeasure();
29615                 }
29616             }
29617             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29618         } 
29619     },
29620     /**
29621      * Sets this button's click handler
29622      * @param {Function} handler The function to call when the button is clicked
29623      * @param {Object} scope (optional) Scope for the function passed above
29624      */
29625     setHandler : function(handler, scope){
29626         this.handler = handler;
29627         this.scope = scope;  
29628     },
29629     
29630     /**
29631      * Sets this button's arrow click handler
29632      * @param {Function} handler The function to call when the arrow is clicked
29633      * @param {Object} scope (optional) Scope for the function passed above
29634      */
29635     setArrowHandler : function(handler, scope){
29636         this.arrowHandler = handler;
29637         this.scope = scope;  
29638     },
29639     
29640     /**
29641      * Focus the button
29642      */
29643     focus : function(){
29644         if(this.el){
29645             this.el.child("button:first").focus();
29646         }
29647     },
29648
29649     // private
29650     onClick : function(e){
29651         e.preventDefault();
29652         if(!this.disabled){
29653             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29654                 if(this.menu && !this.menu.isVisible()){
29655                     this.menu.show(this.el, this.menuAlign);
29656                 }
29657                 this.fireEvent("arrowclick", this, e);
29658                 if(this.arrowHandler){
29659                     this.arrowHandler.call(this.scope || this, this, e);
29660                 }
29661             }else{
29662                 this.fireEvent("click", this, e);
29663                 if(this.handler){
29664                     this.handler.call(this.scope || this, this, e);
29665                 }
29666             }
29667         }
29668     },
29669     // private
29670     onMouseDown : function(e){
29671         if(!this.disabled){
29672             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29673         }
29674     },
29675     // private
29676     onMouseUp : function(e){
29677         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29678     }   
29679 });
29680
29681
29682 // backwards compat
29683 Roo.MenuButton = Roo.SplitButton;/*
29684  * Based on:
29685  * Ext JS Library 1.1.1
29686  * Copyright(c) 2006-2007, Ext JS, LLC.
29687  *
29688  * Originally Released Under LGPL - original licence link has changed is not relivant.
29689  *
29690  * Fork - LGPL
29691  * <script type="text/javascript">
29692  */
29693
29694 /**
29695  * @class Roo.Toolbar
29696  * Basic Toolbar class.
29697  * @constructor
29698  * Creates a new Toolbar
29699  * @param {Object} container The config object
29700  */ 
29701 Roo.Toolbar = function(container, buttons, config)
29702 {
29703     /// old consturctor format still supported..
29704     if(container instanceof Array){ // omit the container for later rendering
29705         buttons = container;
29706         config = buttons;
29707         container = null;
29708     }
29709     if (typeof(container) == 'object' && container.xtype) {
29710         config = container;
29711         container = config.container;
29712         buttons = config.buttons || []; // not really - use items!!
29713     }
29714     var xitems = [];
29715     if (config && config.items) {
29716         xitems = config.items;
29717         delete config.items;
29718     }
29719     Roo.apply(this, config);
29720     this.buttons = buttons;
29721     
29722     if(container){
29723         this.render(container);
29724     }
29725     this.xitems = xitems;
29726     Roo.each(xitems, function(b) {
29727         this.add(b);
29728     }, this);
29729     
29730 };
29731
29732 Roo.Toolbar.prototype = {
29733     /**
29734      * @cfg {Array} items
29735      * array of button configs or elements to add (will be converted to a MixedCollection)
29736      */
29737     
29738     /**
29739      * @cfg {String/HTMLElement/Element} container
29740      * The id or element that will contain the toolbar
29741      */
29742     // private
29743     render : function(ct){
29744         this.el = Roo.get(ct);
29745         if(this.cls){
29746             this.el.addClass(this.cls);
29747         }
29748         // using a table allows for vertical alignment
29749         // 100% width is needed by Safari...
29750         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29751         this.tr = this.el.child("tr", true);
29752         var autoId = 0;
29753         this.items = new Roo.util.MixedCollection(false, function(o){
29754             return o.id || ("item" + (++autoId));
29755         });
29756         if(this.buttons){
29757             this.add.apply(this, this.buttons);
29758             delete this.buttons;
29759         }
29760     },
29761
29762     /**
29763      * Adds element(s) to the toolbar -- this function takes a variable number of 
29764      * arguments of mixed type and adds them to the toolbar.
29765      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29766      * <ul>
29767      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29768      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29769      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29770      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29771      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29772      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29773      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29774      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29775      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29776      * </ul>
29777      * @param {Mixed} arg2
29778      * @param {Mixed} etc.
29779      */
29780     add : function(){
29781         var a = arguments, l = a.length;
29782         for(var i = 0; i < l; i++){
29783             this._add(a[i]);
29784         }
29785     },
29786     // private..
29787     _add : function(el) {
29788         
29789         if (el.xtype) {
29790             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29791         }
29792         
29793         if (el.applyTo){ // some kind of form field
29794             return this.addField(el);
29795         } 
29796         if (el.render){ // some kind of Toolbar.Item
29797             return this.addItem(el);
29798         }
29799         if (typeof el == "string"){ // string
29800             if(el == "separator" || el == "-"){
29801                 return this.addSeparator();
29802             }
29803             if (el == " "){
29804                 return this.addSpacer();
29805             }
29806             if(el == "->"){
29807                 return this.addFill();
29808             }
29809             return this.addText(el);
29810             
29811         }
29812         if(el.tagName){ // element
29813             return this.addElement(el);
29814         }
29815         if(typeof el == "object"){ // must be button config?
29816             return this.addButton(el);
29817         }
29818         // and now what?!?!
29819         return false;
29820         
29821     },
29822     
29823     /**
29824      * Add an Xtype element
29825      * @param {Object} xtype Xtype Object
29826      * @return {Object} created Object
29827      */
29828     addxtype : function(e){
29829         return this.add(e);  
29830     },
29831     
29832     /**
29833      * Returns the Element for this toolbar.
29834      * @return {Roo.Element}
29835      */
29836     getEl : function(){
29837         return this.el;  
29838     },
29839     
29840     /**
29841      * Adds a separator
29842      * @return {Roo.Toolbar.Item} The separator item
29843      */
29844     addSeparator : function(){
29845         return this.addItem(new Roo.Toolbar.Separator());
29846     },
29847
29848     /**
29849      * Adds a spacer element
29850      * @return {Roo.Toolbar.Spacer} The spacer item
29851      */
29852     addSpacer : function(){
29853         return this.addItem(new Roo.Toolbar.Spacer());
29854     },
29855
29856     /**
29857      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29858      * @return {Roo.Toolbar.Fill} The fill item
29859      */
29860     addFill : function(){
29861         return this.addItem(new Roo.Toolbar.Fill());
29862     },
29863
29864     /**
29865      * Adds any standard HTML element to the toolbar
29866      * @param {String/HTMLElement/Element} el The element or id of the element to add
29867      * @return {Roo.Toolbar.Item} The element's item
29868      */
29869     addElement : function(el){
29870         return this.addItem(new Roo.Toolbar.Item(el));
29871     },
29872     /**
29873      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29874      * @type Roo.util.MixedCollection  
29875      */
29876     items : false,
29877      
29878     /**
29879      * Adds any Toolbar.Item or subclass
29880      * @param {Roo.Toolbar.Item} item
29881      * @return {Roo.Toolbar.Item} The item
29882      */
29883     addItem : function(item){
29884         var td = this.nextBlock();
29885         item.render(td);
29886         this.items.add(item);
29887         return item;
29888     },
29889     
29890     /**
29891      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29892      * @param {Object/Array} config A button config or array of configs
29893      * @return {Roo.Toolbar.Button/Array}
29894      */
29895     addButton : function(config){
29896         if(config instanceof Array){
29897             var buttons = [];
29898             for(var i = 0, len = config.length; i < len; i++) {
29899                 buttons.push(this.addButton(config[i]));
29900             }
29901             return buttons;
29902         }
29903         var b = config;
29904         if(!(config instanceof Roo.Toolbar.Button)){
29905             b = config.split ?
29906                 new Roo.Toolbar.SplitButton(config) :
29907                 new Roo.Toolbar.Button(config);
29908         }
29909         var td = this.nextBlock();
29910         b.render(td);
29911         this.items.add(b);
29912         return b;
29913     },
29914     
29915     /**
29916      * Adds text to the toolbar
29917      * @param {String} text The text to add
29918      * @return {Roo.Toolbar.Item} The element's item
29919      */
29920     addText : function(text){
29921         return this.addItem(new Roo.Toolbar.TextItem(text));
29922     },
29923     
29924     /**
29925      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29926      * @param {Number} index The index where the item is to be inserted
29927      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29928      * @return {Roo.Toolbar.Button/Item}
29929      */
29930     insertButton : function(index, item){
29931         if(item instanceof Array){
29932             var buttons = [];
29933             for(var i = 0, len = item.length; i < len; i++) {
29934                buttons.push(this.insertButton(index + i, item[i]));
29935             }
29936             return buttons;
29937         }
29938         if (!(item instanceof Roo.Toolbar.Button)){
29939            item = new Roo.Toolbar.Button(item);
29940         }
29941         var td = document.createElement("td");
29942         this.tr.insertBefore(td, this.tr.childNodes[index]);
29943         item.render(td);
29944         this.items.insert(index, item);
29945         return item;
29946     },
29947     
29948     /**
29949      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29950      * @param {Object} config
29951      * @return {Roo.Toolbar.Item} The element's item
29952      */
29953     addDom : function(config, returnEl){
29954         var td = this.nextBlock();
29955         Roo.DomHelper.overwrite(td, config);
29956         var ti = new Roo.Toolbar.Item(td.firstChild);
29957         ti.render(td);
29958         this.items.add(ti);
29959         return ti;
29960     },
29961
29962     /**
29963      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29964      * @type Roo.util.MixedCollection  
29965      */
29966     fields : false,
29967     
29968     /**
29969      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29970      * Note: the field should not have been rendered yet. For a field that has already been
29971      * rendered, use {@link #addElement}.
29972      * @param {Roo.form.Field} field
29973      * @return {Roo.ToolbarItem}
29974      */
29975      
29976       
29977     addField : function(field) {
29978         if (!this.fields) {
29979             var autoId = 0;
29980             this.fields = new Roo.util.MixedCollection(false, function(o){
29981                 return o.id || ("item" + (++autoId));
29982             });
29983
29984         }
29985         
29986         var td = this.nextBlock();
29987         field.render(td);
29988         var ti = new Roo.Toolbar.Item(td.firstChild);
29989         ti.render(td);
29990         this.items.add(ti);
29991         this.fields.add(field);
29992         return ti;
29993     },
29994     /**
29995      * Hide the toolbar
29996      * @method hide
29997      */
29998      
29999       
30000     hide : function()
30001     {
30002         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30003         this.el.child('div').hide();
30004     },
30005     /**
30006      * Show the toolbar
30007      * @method show
30008      */
30009     show : function()
30010     {
30011         this.el.child('div').show();
30012     },
30013       
30014     // private
30015     nextBlock : function(){
30016         var td = document.createElement("td");
30017         this.tr.appendChild(td);
30018         return td;
30019     },
30020
30021     // private
30022     destroy : function(){
30023         if(this.items){ // rendered?
30024             Roo.destroy.apply(Roo, this.items.items);
30025         }
30026         if(this.fields){ // rendered?
30027             Roo.destroy.apply(Roo, this.fields.items);
30028         }
30029         Roo.Element.uncache(this.el, this.tr);
30030     }
30031 };
30032
30033 /**
30034  * @class Roo.Toolbar.Item
30035  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30036  * @constructor
30037  * Creates a new Item
30038  * @param {HTMLElement} el 
30039  */
30040 Roo.Toolbar.Item = function(el){
30041     var cfg = {};
30042     if (typeof (el.xtype) != 'undefined') {
30043         cfg = el;
30044         el = cfg.el;
30045     }
30046     
30047     this.el = Roo.getDom(el);
30048     this.id = Roo.id(this.el);
30049     this.hidden = false;
30050     
30051     this.addEvents({
30052          /**
30053              * @event render
30054              * Fires when the button is rendered
30055              * @param {Button} this
30056              */
30057         'render': true
30058     });
30059     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30060 };
30061 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30062 //Roo.Toolbar.Item.prototype = {
30063     
30064     /**
30065      * Get this item's HTML Element
30066      * @return {HTMLElement}
30067      */
30068     getEl : function(){
30069        return this.el;  
30070     },
30071
30072     // private
30073     render : function(td){
30074         
30075          this.td = td;
30076         td.appendChild(this.el);
30077         
30078         this.fireEvent('render', this);
30079     },
30080     
30081     /**
30082      * Removes and destroys this item.
30083      */
30084     destroy : function(){
30085         this.td.parentNode.removeChild(this.td);
30086     },
30087     
30088     /**
30089      * Shows this item.
30090      */
30091     show: function(){
30092         this.hidden = false;
30093         this.td.style.display = "";
30094     },
30095     
30096     /**
30097      * Hides this item.
30098      */
30099     hide: function(){
30100         this.hidden = true;
30101         this.td.style.display = "none";
30102     },
30103     
30104     /**
30105      * Convenience function for boolean show/hide.
30106      * @param {Boolean} visible true to show/false to hide
30107      */
30108     setVisible: function(visible){
30109         if(visible) {
30110             this.show();
30111         }else{
30112             this.hide();
30113         }
30114     },
30115     
30116     /**
30117      * Try to focus this item.
30118      */
30119     focus : function(){
30120         Roo.fly(this.el).focus();
30121     },
30122     
30123     /**
30124      * Disables this item.
30125      */
30126     disable : function(){
30127         Roo.fly(this.td).addClass("x-item-disabled");
30128         this.disabled = true;
30129         this.el.disabled = true;
30130     },
30131     
30132     /**
30133      * Enables this item.
30134      */
30135     enable : function(){
30136         Roo.fly(this.td).removeClass("x-item-disabled");
30137         this.disabled = false;
30138         this.el.disabled = false;
30139     }
30140 });
30141
30142
30143 /**
30144  * @class Roo.Toolbar.Separator
30145  * @extends Roo.Toolbar.Item
30146  * A simple toolbar separator class
30147  * @constructor
30148  * Creates a new Separator
30149  */
30150 Roo.Toolbar.Separator = function(cfg){
30151     
30152     var s = document.createElement("span");
30153     s.className = "ytb-sep";
30154     if (cfg) {
30155         cfg.el = s;
30156     }
30157     
30158     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30159 };
30160 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30161     enable:Roo.emptyFn,
30162     disable:Roo.emptyFn,
30163     focus:Roo.emptyFn
30164 });
30165
30166 /**
30167  * @class Roo.Toolbar.Spacer
30168  * @extends Roo.Toolbar.Item
30169  * A simple element that adds extra horizontal space to a toolbar.
30170  * @constructor
30171  * Creates a new Spacer
30172  */
30173 Roo.Toolbar.Spacer = function(cfg){
30174     var s = document.createElement("div");
30175     s.className = "ytb-spacer";
30176     if (cfg) {
30177         cfg.el = s;
30178     }
30179     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30180 };
30181 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30182     enable:Roo.emptyFn,
30183     disable:Roo.emptyFn,
30184     focus:Roo.emptyFn
30185 });
30186
30187 /**
30188  * @class Roo.Toolbar.Fill
30189  * @extends Roo.Toolbar.Spacer
30190  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30191  * @constructor
30192  * Creates a new Spacer
30193  */
30194 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30195     // private
30196     render : function(td){
30197         td.style.width = '100%';
30198         Roo.Toolbar.Fill.superclass.render.call(this, td);
30199     }
30200 });
30201
30202 /**
30203  * @class Roo.Toolbar.TextItem
30204  * @extends Roo.Toolbar.Item
30205  * A simple class that renders text directly into a toolbar.
30206  * @constructor
30207  * Creates a new TextItem
30208  * @param {String} text
30209  */
30210 Roo.Toolbar.TextItem = function(cfg){
30211     var  text = cfg || "";
30212     if (typeof(cfg) == 'object') {
30213         text = cfg.text || "";
30214     }  else {
30215         cfg = null;
30216     }
30217     var s = document.createElement("span");
30218     s.className = "ytb-text";
30219     s.innerHTML = text;
30220     if (cfg) {
30221         cfg.el  = s;
30222     }
30223     
30224     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30225 };
30226 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30227     
30228      
30229     enable:Roo.emptyFn,
30230     disable:Roo.emptyFn,
30231     focus:Roo.emptyFn
30232 });
30233
30234 /**
30235  * @class Roo.Toolbar.Button
30236  * @extends Roo.Button
30237  * A button that renders into a toolbar.
30238  * @constructor
30239  * Creates a new Button
30240  * @param {Object} config A standard {@link Roo.Button} config object
30241  */
30242 Roo.Toolbar.Button = function(config){
30243     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30244 };
30245 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30246     render : function(td){
30247         this.td = td;
30248         Roo.Toolbar.Button.superclass.render.call(this, td);
30249     },
30250     
30251     /**
30252      * Removes and destroys this button
30253      */
30254     destroy : function(){
30255         Roo.Toolbar.Button.superclass.destroy.call(this);
30256         this.td.parentNode.removeChild(this.td);
30257     },
30258     
30259     /**
30260      * Shows this button
30261      */
30262     show: function(){
30263         this.hidden = false;
30264         this.td.style.display = "";
30265     },
30266     
30267     /**
30268      * Hides this button
30269      */
30270     hide: function(){
30271         this.hidden = true;
30272         this.td.style.display = "none";
30273     },
30274
30275     /**
30276      * Disables this item
30277      */
30278     disable : function(){
30279         Roo.fly(this.td).addClass("x-item-disabled");
30280         this.disabled = true;
30281     },
30282
30283     /**
30284      * Enables this item
30285      */
30286     enable : function(){
30287         Roo.fly(this.td).removeClass("x-item-disabled");
30288         this.disabled = false;
30289     }
30290 });
30291 // backwards compat
30292 Roo.ToolbarButton = Roo.Toolbar.Button;
30293
30294 /**
30295  * @class Roo.Toolbar.SplitButton
30296  * @extends Roo.SplitButton
30297  * A menu button that renders into a toolbar.
30298  * @constructor
30299  * Creates a new SplitButton
30300  * @param {Object} config A standard {@link Roo.SplitButton} config object
30301  */
30302 Roo.Toolbar.SplitButton = function(config){
30303     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30304 };
30305 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30306     render : function(td){
30307         this.td = td;
30308         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30309     },
30310     
30311     /**
30312      * Removes and destroys this button
30313      */
30314     destroy : function(){
30315         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30316         this.td.parentNode.removeChild(this.td);
30317     },
30318     
30319     /**
30320      * Shows this button
30321      */
30322     show: function(){
30323         this.hidden = false;
30324         this.td.style.display = "";
30325     },
30326     
30327     /**
30328      * Hides this button
30329      */
30330     hide: function(){
30331         this.hidden = true;
30332         this.td.style.display = "none";
30333     }
30334 });
30335
30336 // backwards compat
30337 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30338  * Based on:
30339  * Ext JS Library 1.1.1
30340  * Copyright(c) 2006-2007, Ext JS, LLC.
30341  *
30342  * Originally Released Under LGPL - original licence link has changed is not relivant.
30343  *
30344  * Fork - LGPL
30345  * <script type="text/javascript">
30346  */
30347  
30348 /**
30349  * @class Roo.PagingToolbar
30350  * @extends Roo.Toolbar
30351  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30352  * @constructor
30353  * Create a new PagingToolbar
30354  * @param {Object} config The config object
30355  */
30356 Roo.PagingToolbar = function(el, ds, config)
30357 {
30358     // old args format still supported... - xtype is prefered..
30359     if (typeof(el) == 'object' && el.xtype) {
30360         // created from xtype...
30361         config = el;
30362         ds = el.dataSource;
30363         el = config.container;
30364     }
30365     var items = [];
30366     if (config.items) {
30367         items = config.items;
30368         config.items = [];
30369     }
30370     
30371     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30372     this.ds = ds;
30373     this.cursor = 0;
30374     this.renderButtons(this.el);
30375     this.bind(ds);
30376     
30377     // supprot items array.
30378    
30379     Roo.each(items, function(e) {
30380         this.add(Roo.factory(e));
30381     },this);
30382     
30383 };
30384
30385 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30386     /**
30387      * @cfg {Roo.data.Store} dataSource
30388      * The underlying data store providing the paged data
30389      */
30390     /**
30391      * @cfg {String/HTMLElement/Element} container
30392      * container The id or element that will contain the toolbar
30393      */
30394     /**
30395      * @cfg {Boolean} displayInfo
30396      * True to display the displayMsg (defaults to false)
30397      */
30398     /**
30399      * @cfg {Number} pageSize
30400      * The number of records to display per page (defaults to 20)
30401      */
30402     pageSize: 20,
30403     /**
30404      * @cfg {String} displayMsg
30405      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30406      */
30407     displayMsg : 'Displaying {0} - {1} of {2}',
30408     /**
30409      * @cfg {String} emptyMsg
30410      * The message to display when no records are found (defaults to "No data to display")
30411      */
30412     emptyMsg : 'No data to display',
30413     /**
30414      * Customizable piece of the default paging text (defaults to "Page")
30415      * @type String
30416      */
30417     beforePageText : "Page",
30418     /**
30419      * Customizable piece of the default paging text (defaults to "of %0")
30420      * @type String
30421      */
30422     afterPageText : "of {0}",
30423     /**
30424      * Customizable piece of the default paging text (defaults to "First Page")
30425      * @type String
30426      */
30427     firstText : "First Page",
30428     /**
30429      * Customizable piece of the default paging text (defaults to "Previous Page")
30430      * @type String
30431      */
30432     prevText : "Previous Page",
30433     /**
30434      * Customizable piece of the default paging text (defaults to "Next Page")
30435      * @type String
30436      */
30437     nextText : "Next Page",
30438     /**
30439      * Customizable piece of the default paging text (defaults to "Last Page")
30440      * @type String
30441      */
30442     lastText : "Last Page",
30443     /**
30444      * Customizable piece of the default paging text (defaults to "Refresh")
30445      * @type String
30446      */
30447     refreshText : "Refresh",
30448
30449     // private
30450     renderButtons : function(el){
30451         Roo.PagingToolbar.superclass.render.call(this, el);
30452         this.first = this.addButton({
30453             tooltip: this.firstText,
30454             cls: "x-btn-icon x-grid-page-first",
30455             disabled: true,
30456             handler: this.onClick.createDelegate(this, ["first"])
30457         });
30458         this.prev = this.addButton({
30459             tooltip: this.prevText,
30460             cls: "x-btn-icon x-grid-page-prev",
30461             disabled: true,
30462             handler: this.onClick.createDelegate(this, ["prev"])
30463         });
30464         //this.addSeparator();
30465         this.add(this.beforePageText);
30466         this.field = Roo.get(this.addDom({
30467            tag: "input",
30468            type: "text",
30469            size: "3",
30470            value: "1",
30471            cls: "x-grid-page-number"
30472         }).el);
30473         this.field.on("keydown", this.onPagingKeydown, this);
30474         this.field.on("focus", function(){this.dom.select();});
30475         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30476         this.field.setHeight(18);
30477         //this.addSeparator();
30478         this.next = this.addButton({
30479             tooltip: this.nextText,
30480             cls: "x-btn-icon x-grid-page-next",
30481             disabled: true,
30482             handler: this.onClick.createDelegate(this, ["next"])
30483         });
30484         this.last = this.addButton({
30485             tooltip: this.lastText,
30486             cls: "x-btn-icon x-grid-page-last",
30487             disabled: true,
30488             handler: this.onClick.createDelegate(this, ["last"])
30489         });
30490         //this.addSeparator();
30491         this.loading = this.addButton({
30492             tooltip: this.refreshText,
30493             cls: "x-btn-icon x-grid-loading",
30494             handler: this.onClick.createDelegate(this, ["refresh"])
30495         });
30496
30497         if(this.displayInfo){
30498             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30499         }
30500     },
30501
30502     // private
30503     updateInfo : function(){
30504         if(this.displayEl){
30505             var count = this.ds.getCount();
30506             var msg = count == 0 ?
30507                 this.emptyMsg :
30508                 String.format(
30509                     this.displayMsg,
30510                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30511                 );
30512             this.displayEl.update(msg);
30513         }
30514     },
30515
30516     // private
30517     onLoad : function(ds, r, o){
30518        this.cursor = o.params ? o.params.start : 0;
30519        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30520
30521        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30522        this.field.dom.value = ap;
30523        this.first.setDisabled(ap == 1);
30524        this.prev.setDisabled(ap == 1);
30525        this.next.setDisabled(ap == ps);
30526        this.last.setDisabled(ap == ps);
30527        this.loading.enable();
30528        this.updateInfo();
30529     },
30530
30531     // private
30532     getPageData : function(){
30533         var total = this.ds.getTotalCount();
30534         return {
30535             total : total,
30536             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30537             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30538         };
30539     },
30540
30541     // private
30542     onLoadError : function(){
30543         this.loading.enable();
30544     },
30545
30546     // private
30547     onPagingKeydown : function(e){
30548         var k = e.getKey();
30549         var d = this.getPageData();
30550         if(k == e.RETURN){
30551             var v = this.field.dom.value, pageNum;
30552             if(!v || isNaN(pageNum = parseInt(v, 10))){
30553                 this.field.dom.value = d.activePage;
30554                 return;
30555             }
30556             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30557             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30558             e.stopEvent();
30559         }
30560         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))
30561         {
30562           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30563           this.field.dom.value = pageNum;
30564           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30565           e.stopEvent();
30566         }
30567         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30568         {
30569           var v = this.field.dom.value, pageNum; 
30570           var increment = (e.shiftKey) ? 10 : 1;
30571           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30572             increment *= -1;
30573           }
30574           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30575             this.field.dom.value = d.activePage;
30576             return;
30577           }
30578           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30579           {
30580             this.field.dom.value = parseInt(v, 10) + increment;
30581             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30582             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30583           }
30584           e.stopEvent();
30585         }
30586     },
30587
30588     // private
30589     beforeLoad : function(){
30590         if(this.loading){
30591             this.loading.disable();
30592         }
30593     },
30594
30595     // private
30596     onClick : function(which){
30597         var ds = this.ds;
30598         switch(which){
30599             case "first":
30600                 ds.load({params:{start: 0, limit: this.pageSize}});
30601             break;
30602             case "prev":
30603                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30604             break;
30605             case "next":
30606                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30607             break;
30608             case "last":
30609                 var total = ds.getTotalCount();
30610                 var extra = total % this.pageSize;
30611                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30612                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30613             break;
30614             case "refresh":
30615                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30616             break;
30617         }
30618     },
30619
30620     /**
30621      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30622      * @param {Roo.data.Store} store The data store to unbind
30623      */
30624     unbind : function(ds){
30625         ds.un("beforeload", this.beforeLoad, this);
30626         ds.un("load", this.onLoad, this);
30627         ds.un("loadexception", this.onLoadError, this);
30628         ds.un("remove", this.updateInfo, this);
30629         ds.un("add", this.updateInfo, this);
30630         this.ds = undefined;
30631     },
30632
30633     /**
30634      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30635      * @param {Roo.data.Store} store The data store to bind
30636      */
30637     bind : function(ds){
30638         ds.on("beforeload", this.beforeLoad, this);
30639         ds.on("load", this.onLoad, this);
30640         ds.on("loadexception", this.onLoadError, this);
30641         ds.on("remove", this.updateInfo, this);
30642         ds.on("add", this.updateInfo, this);
30643         this.ds = ds;
30644     }
30645 });/*
30646  * Based on:
30647  * Ext JS Library 1.1.1
30648  * Copyright(c) 2006-2007, Ext JS, LLC.
30649  *
30650  * Originally Released Under LGPL - original licence link has changed is not relivant.
30651  *
30652  * Fork - LGPL
30653  * <script type="text/javascript">
30654  */
30655
30656 /**
30657  * @class Roo.Resizable
30658  * @extends Roo.util.Observable
30659  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30660  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30661  * 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
30662  * the element will be wrapped for you automatically.</p>
30663  * <p>Here is the list of valid resize handles:</p>
30664  * <pre>
30665 Value   Description
30666 ------  -------------------
30667  'n'     north
30668  's'     south
30669  'e'     east
30670  'w'     west
30671  'nw'    northwest
30672  'sw'    southwest
30673  'se'    southeast
30674  'ne'    northeast
30675  'hd'    horizontal drag
30676  'all'   all
30677 </pre>
30678  * <p>Here's an example showing the creation of a typical Resizable:</p>
30679  * <pre><code>
30680 var resizer = new Roo.Resizable("element-id", {
30681     handles: 'all',
30682     minWidth: 200,
30683     minHeight: 100,
30684     maxWidth: 500,
30685     maxHeight: 400,
30686     pinned: true
30687 });
30688 resizer.on("resize", myHandler);
30689 </code></pre>
30690  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30691  * resizer.east.setDisplayed(false);</p>
30692  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30693  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30694  * resize operation's new size (defaults to [0, 0])
30695  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30696  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30697  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30698  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30699  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30700  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30701  * @cfg {Number} width The width of the element in pixels (defaults to null)
30702  * @cfg {Number} height The height of the element in pixels (defaults to null)
30703  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30704  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30705  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30706  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30707  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30708  * in favor of the handles config option (defaults to false)
30709  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30710  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30711  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30712  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30713  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30714  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30715  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30716  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30717  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30718  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30719  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30720  * @constructor
30721  * Create a new resizable component
30722  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30723  * @param {Object} config configuration options
30724   */
30725 Roo.Resizable = function(el, config)
30726 {
30727     this.el = Roo.get(el);
30728
30729     if(config && config.wrap){
30730         config.resizeChild = this.el;
30731         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30732         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30733         this.el.setStyle("overflow", "hidden");
30734         this.el.setPositioning(config.resizeChild.getPositioning());
30735         config.resizeChild.clearPositioning();
30736         if(!config.width || !config.height){
30737             var csize = config.resizeChild.getSize();
30738             this.el.setSize(csize.width, csize.height);
30739         }
30740         if(config.pinned && !config.adjustments){
30741             config.adjustments = "auto";
30742         }
30743     }
30744
30745     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30746     this.proxy.unselectable();
30747     this.proxy.enableDisplayMode('block');
30748
30749     Roo.apply(this, config);
30750
30751     if(this.pinned){
30752         this.disableTrackOver = true;
30753         this.el.addClass("x-resizable-pinned");
30754     }
30755     // if the element isn't positioned, make it relative
30756     var position = this.el.getStyle("position");
30757     if(position != "absolute" && position != "fixed"){
30758         this.el.setStyle("position", "relative");
30759     }
30760     if(!this.handles){ // no handles passed, must be legacy style
30761         this.handles = 's,e,se';
30762         if(this.multiDirectional){
30763             this.handles += ',n,w';
30764         }
30765     }
30766     if(this.handles == "all"){
30767         this.handles = "n s e w ne nw se sw";
30768     }
30769     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30770     var ps = Roo.Resizable.positions;
30771     for(var i = 0, len = hs.length; i < len; i++){
30772         if(hs[i] && ps[hs[i]]){
30773             var pos = ps[hs[i]];
30774             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30775         }
30776     }
30777     // legacy
30778     this.corner = this.southeast;
30779     
30780     // updateBox = the box can move..
30781     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30782         this.updateBox = true;
30783     }
30784
30785     this.activeHandle = null;
30786
30787     if(this.resizeChild){
30788         if(typeof this.resizeChild == "boolean"){
30789             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30790         }else{
30791             this.resizeChild = Roo.get(this.resizeChild, true);
30792         }
30793     }
30794     
30795     if(this.adjustments == "auto"){
30796         var rc = this.resizeChild;
30797         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30798         if(rc && (hw || hn)){
30799             rc.position("relative");
30800             rc.setLeft(hw ? hw.el.getWidth() : 0);
30801             rc.setTop(hn ? hn.el.getHeight() : 0);
30802         }
30803         this.adjustments = [
30804             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30805             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30806         ];
30807     }
30808
30809     if(this.draggable){
30810         this.dd = this.dynamic ?
30811             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30812         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30813     }
30814
30815     // public events
30816     this.addEvents({
30817         /**
30818          * @event beforeresize
30819          * Fired before resize is allowed. Set enabled to false to cancel resize.
30820          * @param {Roo.Resizable} this
30821          * @param {Roo.EventObject} e The mousedown event
30822          */
30823         "beforeresize" : true,
30824         /**
30825          * @event resizing
30826          * Fired a resizing.
30827          * @param {Roo.Resizable} this
30828          * @param {Number} x The new x position
30829          * @param {Number} y The new y position
30830          * @param {Number} w The new w width
30831          * @param {Number} h The new h hight
30832          * @param {Roo.EventObject} e The mouseup event
30833          */
30834         "resizing" : true,
30835         /**
30836          * @event resize
30837          * Fired after a resize.
30838          * @param {Roo.Resizable} this
30839          * @param {Number} width The new width
30840          * @param {Number} height The new height
30841          * @param {Roo.EventObject} e The mouseup event
30842          */
30843         "resize" : true
30844     });
30845
30846     if(this.width !== null && this.height !== null){
30847         this.resizeTo(this.width, this.height);
30848     }else{
30849         this.updateChildSize();
30850     }
30851     if(Roo.isIE){
30852         this.el.dom.style.zoom = 1;
30853     }
30854     Roo.Resizable.superclass.constructor.call(this);
30855 };
30856
30857 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30858         resizeChild : false,
30859         adjustments : [0, 0],
30860         minWidth : 5,
30861         minHeight : 5,
30862         maxWidth : 10000,
30863         maxHeight : 10000,
30864         enabled : true,
30865         animate : false,
30866         duration : .35,
30867         dynamic : false,
30868         handles : false,
30869         multiDirectional : false,
30870         disableTrackOver : false,
30871         easing : 'easeOutStrong',
30872         widthIncrement : 0,
30873         heightIncrement : 0,
30874         pinned : false,
30875         width : null,
30876         height : null,
30877         preserveRatio : false,
30878         transparent: false,
30879         minX: 0,
30880         minY: 0,
30881         draggable: false,
30882
30883         /**
30884          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30885          */
30886         constrainTo: undefined,
30887         /**
30888          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30889          */
30890         resizeRegion: undefined,
30891
30892
30893     /**
30894      * Perform a manual resize
30895      * @param {Number} width
30896      * @param {Number} height
30897      */
30898     resizeTo : function(width, height){
30899         this.el.setSize(width, height);
30900         this.updateChildSize();
30901         this.fireEvent("resize", this, width, height, null);
30902     },
30903
30904     // private
30905     startSizing : function(e, handle){
30906         this.fireEvent("beforeresize", this, e);
30907         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30908
30909             if(!this.overlay){
30910                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30911                 this.overlay.unselectable();
30912                 this.overlay.enableDisplayMode("block");
30913                 this.overlay.on("mousemove", this.onMouseMove, this);
30914                 this.overlay.on("mouseup", this.onMouseUp, this);
30915             }
30916             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30917
30918             this.resizing = true;
30919             this.startBox = this.el.getBox();
30920             this.startPoint = e.getXY();
30921             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30922                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30923
30924             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30925             this.overlay.show();
30926
30927             if(this.constrainTo) {
30928                 var ct = Roo.get(this.constrainTo);
30929                 this.resizeRegion = ct.getRegion().adjust(
30930                     ct.getFrameWidth('t'),
30931                     ct.getFrameWidth('l'),
30932                     -ct.getFrameWidth('b'),
30933                     -ct.getFrameWidth('r')
30934                 );
30935             }
30936
30937             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30938             this.proxy.show();
30939             this.proxy.setBox(this.startBox);
30940             if(!this.dynamic){
30941                 this.proxy.setStyle('visibility', 'visible');
30942             }
30943         }
30944     },
30945
30946     // private
30947     onMouseDown : function(handle, e){
30948         if(this.enabled){
30949             e.stopEvent();
30950             this.activeHandle = handle;
30951             this.startSizing(e, handle);
30952         }
30953     },
30954
30955     // private
30956     onMouseUp : function(e){
30957         var size = this.resizeElement();
30958         this.resizing = false;
30959         this.handleOut();
30960         this.overlay.hide();
30961         this.proxy.hide();
30962         this.fireEvent("resize", this, size.width, size.height, e);
30963     },
30964
30965     // private
30966     updateChildSize : function(){
30967         
30968         if(this.resizeChild){
30969             var el = this.el;
30970             var child = this.resizeChild;
30971             var adj = this.adjustments;
30972             if(el.dom.offsetWidth){
30973                 var b = el.getSize(true);
30974                 child.setSize(b.width+adj[0], b.height+adj[1]);
30975             }
30976             // Second call here for IE
30977             // The first call enables instant resizing and
30978             // the second call corrects scroll bars if they
30979             // exist
30980             if(Roo.isIE){
30981                 setTimeout(function(){
30982                     if(el.dom.offsetWidth){
30983                         var b = el.getSize(true);
30984                         child.setSize(b.width+adj[0], b.height+adj[1]);
30985                     }
30986                 }, 10);
30987             }
30988         }
30989     },
30990
30991     // private
30992     snap : function(value, inc, min){
30993         if(!inc || !value) {
30994             return value;
30995         }
30996         var newValue = value;
30997         var m = value % inc;
30998         if(m > 0){
30999             if(m > (inc/2)){
31000                 newValue = value + (inc-m);
31001             }else{
31002                 newValue = value - m;
31003             }
31004         }
31005         return Math.max(min, newValue);
31006     },
31007
31008     // private
31009     resizeElement : function(){
31010         var box = this.proxy.getBox();
31011         if(this.updateBox){
31012             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31013         }else{
31014             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31015         }
31016         this.updateChildSize();
31017         if(!this.dynamic){
31018             this.proxy.hide();
31019         }
31020         return box;
31021     },
31022
31023     // private
31024     constrain : function(v, diff, m, mx){
31025         if(v - diff < m){
31026             diff = v - m;
31027         }else if(v - diff > mx){
31028             diff = mx - v;
31029         }
31030         return diff;
31031     },
31032
31033     // private
31034     onMouseMove : function(e){
31035         
31036         if(this.enabled){
31037             try{// try catch so if something goes wrong the user doesn't get hung
31038
31039             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31040                 return;
31041             }
31042
31043             //var curXY = this.startPoint;
31044             var curSize = this.curSize || this.startBox;
31045             var x = this.startBox.x, y = this.startBox.y;
31046             var ox = x, oy = y;
31047             var w = curSize.width, h = curSize.height;
31048             var ow = w, oh = h;
31049             var mw = this.minWidth, mh = this.minHeight;
31050             var mxw = this.maxWidth, mxh = this.maxHeight;
31051             var wi = this.widthIncrement;
31052             var hi = this.heightIncrement;
31053
31054             var eventXY = e.getXY();
31055             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31056             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31057
31058             var pos = this.activeHandle.position;
31059
31060             switch(pos){
31061                 case "east":
31062                     w += diffX;
31063                     w = Math.min(Math.max(mw, w), mxw);
31064                     break;
31065              
31066                 case "south":
31067                     h += diffY;
31068                     h = Math.min(Math.max(mh, h), mxh);
31069                     break;
31070                 case "southeast":
31071                     w += diffX;
31072                     h += diffY;
31073                     w = Math.min(Math.max(mw, w), mxw);
31074                     h = Math.min(Math.max(mh, h), mxh);
31075                     break;
31076                 case "north":
31077                     diffY = this.constrain(h, diffY, mh, mxh);
31078                     y += diffY;
31079                     h -= diffY;
31080                     break;
31081                 case "hdrag":
31082                     
31083                     if (wi) {
31084                         var adiffX = Math.abs(diffX);
31085                         var sub = (adiffX % wi); // how much 
31086                         if (sub > (wi/2)) { // far enough to snap
31087                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31088                         } else {
31089                             // remove difference.. 
31090                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31091                         }
31092                     }
31093                     x += diffX;
31094                     x = Math.max(this.minX, x);
31095                     break;
31096                 case "west":
31097                     diffX = this.constrain(w, diffX, mw, mxw);
31098                     x += diffX;
31099                     w -= diffX;
31100                     break;
31101                 case "northeast":
31102                     w += diffX;
31103                     w = Math.min(Math.max(mw, w), mxw);
31104                     diffY = this.constrain(h, diffY, mh, mxh);
31105                     y += diffY;
31106                     h -= diffY;
31107                     break;
31108                 case "northwest":
31109                     diffX = this.constrain(w, diffX, mw, mxw);
31110                     diffY = this.constrain(h, diffY, mh, mxh);
31111                     y += diffY;
31112                     h -= diffY;
31113                     x += diffX;
31114                     w -= diffX;
31115                     break;
31116                case "southwest":
31117                     diffX = this.constrain(w, diffX, mw, mxw);
31118                     h += diffY;
31119                     h = Math.min(Math.max(mh, h), mxh);
31120                     x += diffX;
31121                     w -= diffX;
31122                     break;
31123             }
31124
31125             var sw = this.snap(w, wi, mw);
31126             var sh = this.snap(h, hi, mh);
31127             if(sw != w || sh != h){
31128                 switch(pos){
31129                     case "northeast":
31130                         y -= sh - h;
31131                     break;
31132                     case "north":
31133                         y -= sh - h;
31134                         break;
31135                     case "southwest":
31136                         x -= sw - w;
31137                     break;
31138                     case "west":
31139                         x -= sw - w;
31140                         break;
31141                     case "northwest":
31142                         x -= sw - w;
31143                         y -= sh - h;
31144                     break;
31145                 }
31146                 w = sw;
31147                 h = sh;
31148             }
31149
31150             if(this.preserveRatio){
31151                 switch(pos){
31152                     case "southeast":
31153                     case "east":
31154                         h = oh * (w/ow);
31155                         h = Math.min(Math.max(mh, h), mxh);
31156                         w = ow * (h/oh);
31157                        break;
31158                     case "south":
31159                         w = ow * (h/oh);
31160                         w = Math.min(Math.max(mw, w), mxw);
31161                         h = oh * (w/ow);
31162                         break;
31163                     case "northeast":
31164                         w = ow * (h/oh);
31165                         w = Math.min(Math.max(mw, w), mxw);
31166                         h = oh * (w/ow);
31167                     break;
31168                     case "north":
31169                         var tw = w;
31170                         w = ow * (h/oh);
31171                         w = Math.min(Math.max(mw, w), mxw);
31172                         h = oh * (w/ow);
31173                         x += (tw - w) / 2;
31174                         break;
31175                     case "southwest":
31176                         h = oh * (w/ow);
31177                         h = Math.min(Math.max(mh, h), mxh);
31178                         var tw = w;
31179                         w = ow * (h/oh);
31180                         x += tw - w;
31181                         break;
31182                     case "west":
31183                         var th = h;
31184                         h = oh * (w/ow);
31185                         h = Math.min(Math.max(mh, h), mxh);
31186                         y += (th - h) / 2;
31187                         var tw = w;
31188                         w = ow * (h/oh);
31189                         x += tw - w;
31190                        break;
31191                     case "northwest":
31192                         var tw = w;
31193                         var th = h;
31194                         h = oh * (w/ow);
31195                         h = Math.min(Math.max(mh, h), mxh);
31196                         w = ow * (h/oh);
31197                         y += th - h;
31198                         x += tw - w;
31199                        break;
31200
31201                 }
31202             }
31203             if (pos == 'hdrag') {
31204                 w = ow;
31205             }
31206             this.proxy.setBounds(x, y, w, h);
31207             if(this.dynamic){
31208                 this.resizeElement();
31209             }
31210             }catch(e){}
31211         }
31212         this.fireEvent("resizing", this, x, y, w, h, e);
31213     },
31214
31215     // private
31216     handleOver : function(){
31217         if(this.enabled){
31218             this.el.addClass("x-resizable-over");
31219         }
31220     },
31221
31222     // private
31223     handleOut : function(){
31224         if(!this.resizing){
31225             this.el.removeClass("x-resizable-over");
31226         }
31227     },
31228
31229     /**
31230      * Returns the element this component is bound to.
31231      * @return {Roo.Element}
31232      */
31233     getEl : function(){
31234         return this.el;
31235     },
31236
31237     /**
31238      * Returns the resizeChild element (or null).
31239      * @return {Roo.Element}
31240      */
31241     getResizeChild : function(){
31242         return this.resizeChild;
31243     },
31244     groupHandler : function()
31245     {
31246         
31247     },
31248     /**
31249      * Destroys this resizable. If the element was wrapped and
31250      * removeEl is not true then the element remains.
31251      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31252      */
31253     destroy : function(removeEl){
31254         this.proxy.remove();
31255         if(this.overlay){
31256             this.overlay.removeAllListeners();
31257             this.overlay.remove();
31258         }
31259         var ps = Roo.Resizable.positions;
31260         for(var k in ps){
31261             if(typeof ps[k] != "function" && this[ps[k]]){
31262                 var h = this[ps[k]];
31263                 h.el.removeAllListeners();
31264                 h.el.remove();
31265             }
31266         }
31267         if(removeEl){
31268             this.el.update("");
31269             this.el.remove();
31270         }
31271     }
31272 });
31273
31274 // private
31275 // hash to map config positions to true positions
31276 Roo.Resizable.positions = {
31277     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31278     hd: "hdrag"
31279 };
31280
31281 // private
31282 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31283     if(!this.tpl){
31284         // only initialize the template if resizable is used
31285         var tpl = Roo.DomHelper.createTemplate(
31286             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31287         );
31288         tpl.compile();
31289         Roo.Resizable.Handle.prototype.tpl = tpl;
31290     }
31291     this.position = pos;
31292     this.rz = rz;
31293     // show north drag fro topdra
31294     var handlepos = pos == 'hdrag' ? 'north' : pos;
31295     
31296     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31297     if (pos == 'hdrag') {
31298         this.el.setStyle('cursor', 'pointer');
31299     }
31300     this.el.unselectable();
31301     if(transparent){
31302         this.el.setOpacity(0);
31303     }
31304     this.el.on("mousedown", this.onMouseDown, this);
31305     if(!disableTrackOver){
31306         this.el.on("mouseover", this.onMouseOver, this);
31307         this.el.on("mouseout", this.onMouseOut, this);
31308     }
31309 };
31310
31311 // private
31312 Roo.Resizable.Handle.prototype = {
31313     afterResize : function(rz){
31314         Roo.log('after?');
31315         // do nothing
31316     },
31317     // private
31318     onMouseDown : function(e){
31319         this.rz.onMouseDown(this, e);
31320     },
31321     // private
31322     onMouseOver : function(e){
31323         this.rz.handleOver(this, e);
31324     },
31325     // private
31326     onMouseOut : function(e){
31327         this.rz.handleOut(this, e);
31328     }
31329 };/*
31330  * Based on:
31331  * Ext JS Library 1.1.1
31332  * Copyright(c) 2006-2007, Ext JS, LLC.
31333  *
31334  * Originally Released Under LGPL - original licence link has changed is not relivant.
31335  *
31336  * Fork - LGPL
31337  * <script type="text/javascript">
31338  */
31339
31340 /**
31341  * @class Roo.Editor
31342  * @extends Roo.Component
31343  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31344  * @constructor
31345  * Create a new Editor
31346  * @param {Roo.form.Field} field The Field object (or descendant)
31347  * @param {Object} config The config object
31348  */
31349 Roo.Editor = function(field, config){
31350     Roo.Editor.superclass.constructor.call(this, config);
31351     this.field = field;
31352     this.addEvents({
31353         /**
31354              * @event beforestartedit
31355              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31356              * false from the handler of this event.
31357              * @param {Editor} this
31358              * @param {Roo.Element} boundEl The underlying element bound to this editor
31359              * @param {Mixed} value The field value being set
31360              */
31361         "beforestartedit" : true,
31362         /**
31363              * @event startedit
31364              * Fires when this editor is displayed
31365              * @param {Roo.Element} boundEl The underlying element bound to this editor
31366              * @param {Mixed} value The starting field value
31367              */
31368         "startedit" : true,
31369         /**
31370              * @event beforecomplete
31371              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31372              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31373              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31374              * event will not fire since no edit actually occurred.
31375              * @param {Editor} this
31376              * @param {Mixed} value The current field value
31377              * @param {Mixed} startValue The original field value
31378              */
31379         "beforecomplete" : true,
31380         /**
31381              * @event complete
31382              * Fires after editing is complete and any changed value has been written to the underlying field.
31383              * @param {Editor} this
31384              * @param {Mixed} value The current field value
31385              * @param {Mixed} startValue The original field value
31386              */
31387         "complete" : true,
31388         /**
31389          * @event specialkey
31390          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31391          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31392          * @param {Roo.form.Field} this
31393          * @param {Roo.EventObject} e The event object
31394          */
31395         "specialkey" : true
31396     });
31397 };
31398
31399 Roo.extend(Roo.Editor, Roo.Component, {
31400     /**
31401      * @cfg {Boolean/String} autosize
31402      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31403      * or "height" to adopt the height only (defaults to false)
31404      */
31405     /**
31406      * @cfg {Boolean} revertInvalid
31407      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31408      * validation fails (defaults to true)
31409      */
31410     /**
31411      * @cfg {Boolean} ignoreNoChange
31412      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31413      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31414      * will never be ignored.
31415      */
31416     /**
31417      * @cfg {Boolean} hideEl
31418      * False to keep the bound element visible while the editor is displayed (defaults to true)
31419      */
31420     /**
31421      * @cfg {Mixed} value
31422      * The data value of the underlying field (defaults to "")
31423      */
31424     value : "",
31425     /**
31426      * @cfg {String} alignment
31427      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31428      */
31429     alignment: "c-c?",
31430     /**
31431      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31432      * for bottom-right shadow (defaults to "frame")
31433      */
31434     shadow : "frame",
31435     /**
31436      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31437      */
31438     constrain : false,
31439     /**
31440      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31441      */
31442     completeOnEnter : false,
31443     /**
31444      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31445      */
31446     cancelOnEsc : false,
31447     /**
31448      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31449      */
31450     updateEl : false,
31451
31452     // private
31453     onRender : function(ct, position){
31454         this.el = new Roo.Layer({
31455             shadow: this.shadow,
31456             cls: "x-editor",
31457             parentEl : ct,
31458             shim : this.shim,
31459             shadowOffset:4,
31460             id: this.id,
31461             constrain: this.constrain
31462         });
31463         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31464         if(this.field.msgTarget != 'title'){
31465             this.field.msgTarget = 'qtip';
31466         }
31467         this.field.render(this.el);
31468         if(Roo.isGecko){
31469             this.field.el.dom.setAttribute('autocomplete', 'off');
31470         }
31471         this.field.on("specialkey", this.onSpecialKey, this);
31472         if(this.swallowKeys){
31473             this.field.el.swallowEvent(['keydown','keypress']);
31474         }
31475         this.field.show();
31476         this.field.on("blur", this.onBlur, this);
31477         if(this.field.grow){
31478             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31479         }
31480     },
31481
31482     onSpecialKey : function(field, e)
31483     {
31484         //Roo.log('editor onSpecialKey');
31485         if(this.completeOnEnter && e.getKey() == e.ENTER){
31486             e.stopEvent();
31487             this.completeEdit();
31488             return;
31489         }
31490         // do not fire special key otherwise it might hide close the editor...
31491         if(e.getKey() == e.ENTER){    
31492             return;
31493         }
31494         if(this.cancelOnEsc && e.getKey() == e.ESC){
31495             this.cancelEdit();
31496             return;
31497         } 
31498         this.fireEvent('specialkey', field, e);
31499     
31500     },
31501
31502     /**
31503      * Starts the editing process and shows the editor.
31504      * @param {String/HTMLElement/Element} el The element to edit
31505      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31506       * to the innerHTML of el.
31507      */
31508     startEdit : function(el, value){
31509         if(this.editing){
31510             this.completeEdit();
31511         }
31512         this.boundEl = Roo.get(el);
31513         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31514         if(!this.rendered){
31515             this.render(this.parentEl || document.body);
31516         }
31517         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31518             return;
31519         }
31520         this.startValue = v;
31521         this.field.setValue(v);
31522         if(this.autoSize){
31523             var sz = this.boundEl.getSize();
31524             switch(this.autoSize){
31525                 case "width":
31526                 this.setSize(sz.width,  "");
31527                 break;
31528                 case "height":
31529                 this.setSize("",  sz.height);
31530                 break;
31531                 default:
31532                 this.setSize(sz.width,  sz.height);
31533             }
31534         }
31535         this.el.alignTo(this.boundEl, this.alignment);
31536         this.editing = true;
31537         if(Roo.QuickTips){
31538             Roo.QuickTips.disable();
31539         }
31540         this.show();
31541     },
31542
31543     /**
31544      * Sets the height and width of this editor.
31545      * @param {Number} width The new width
31546      * @param {Number} height The new height
31547      */
31548     setSize : function(w, h){
31549         this.field.setSize(w, h);
31550         if(this.el){
31551             this.el.sync();
31552         }
31553     },
31554
31555     /**
31556      * Realigns the editor to the bound field based on the current alignment config value.
31557      */
31558     realign : function(){
31559         this.el.alignTo(this.boundEl, this.alignment);
31560     },
31561
31562     /**
31563      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31564      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31565      */
31566     completeEdit : function(remainVisible){
31567         if(!this.editing){
31568             return;
31569         }
31570         var v = this.getValue();
31571         if(this.revertInvalid !== false && !this.field.isValid()){
31572             v = this.startValue;
31573             this.cancelEdit(true);
31574         }
31575         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31576             this.editing = false;
31577             this.hide();
31578             return;
31579         }
31580         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31581             this.editing = false;
31582             if(this.updateEl && this.boundEl){
31583                 this.boundEl.update(v);
31584             }
31585             if(remainVisible !== true){
31586                 this.hide();
31587             }
31588             this.fireEvent("complete", this, v, this.startValue);
31589         }
31590     },
31591
31592     // private
31593     onShow : function(){
31594         this.el.show();
31595         if(this.hideEl !== false){
31596             this.boundEl.hide();
31597         }
31598         this.field.show();
31599         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31600             this.fixIEFocus = true;
31601             this.deferredFocus.defer(50, this);
31602         }else{
31603             this.field.focus();
31604         }
31605         this.fireEvent("startedit", this.boundEl, this.startValue);
31606     },
31607
31608     deferredFocus : function(){
31609         if(this.editing){
31610             this.field.focus();
31611         }
31612     },
31613
31614     /**
31615      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31616      * reverted to the original starting value.
31617      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31618      * cancel (defaults to false)
31619      */
31620     cancelEdit : function(remainVisible){
31621         if(this.editing){
31622             this.setValue(this.startValue);
31623             if(remainVisible !== true){
31624                 this.hide();
31625             }
31626         }
31627     },
31628
31629     // private
31630     onBlur : function(){
31631         if(this.allowBlur !== true && this.editing){
31632             this.completeEdit();
31633         }
31634     },
31635
31636     // private
31637     onHide : function(){
31638         if(this.editing){
31639             this.completeEdit();
31640             return;
31641         }
31642         this.field.blur();
31643         if(this.field.collapse){
31644             this.field.collapse();
31645         }
31646         this.el.hide();
31647         if(this.hideEl !== false){
31648             this.boundEl.show();
31649         }
31650         if(Roo.QuickTips){
31651             Roo.QuickTips.enable();
31652         }
31653     },
31654
31655     /**
31656      * Sets the data value of the editor
31657      * @param {Mixed} value Any valid value supported by the underlying field
31658      */
31659     setValue : function(v){
31660         this.field.setValue(v);
31661     },
31662
31663     /**
31664      * Gets the data value of the editor
31665      * @return {Mixed} The data value
31666      */
31667     getValue : function(){
31668         return this.field.getValue();
31669     }
31670 });/*
31671  * Based on:
31672  * Ext JS Library 1.1.1
31673  * Copyright(c) 2006-2007, Ext JS, LLC.
31674  *
31675  * Originally Released Under LGPL - original licence link has changed is not relivant.
31676  *
31677  * Fork - LGPL
31678  * <script type="text/javascript">
31679  */
31680  
31681 /**
31682  * @class Roo.BasicDialog
31683  * @extends Roo.util.Observable
31684  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31685  * <pre><code>
31686 var dlg = new Roo.BasicDialog("my-dlg", {
31687     height: 200,
31688     width: 300,
31689     minHeight: 100,
31690     minWidth: 150,
31691     modal: true,
31692     proxyDrag: true,
31693     shadow: true
31694 });
31695 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31696 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31697 dlg.addButton('Cancel', dlg.hide, dlg);
31698 dlg.show();
31699 </code></pre>
31700   <b>A Dialog should always be a direct child of the body element.</b>
31701  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31702  * @cfg {String} title Default text to display in the title bar (defaults to null)
31703  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31704  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31705  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31706  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31707  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31708  * (defaults to null with no animation)
31709  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31710  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31711  * property for valid values (defaults to 'all')
31712  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31713  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31714  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31715  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31716  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31717  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31718  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31719  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31720  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31721  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31722  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31723  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31724  * draggable = true (defaults to false)
31725  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31726  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31727  * shadow (defaults to false)
31728  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31729  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31730  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31731  * @cfg {Array} buttons Array of buttons
31732  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31733  * @constructor
31734  * Create a new BasicDialog.
31735  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31736  * @param {Object} config Configuration options
31737  */
31738 Roo.BasicDialog = function(el, config){
31739     this.el = Roo.get(el);
31740     var dh = Roo.DomHelper;
31741     if(!this.el && config && config.autoCreate){
31742         if(typeof config.autoCreate == "object"){
31743             if(!config.autoCreate.id){
31744                 config.autoCreate.id = el;
31745             }
31746             this.el = dh.append(document.body,
31747                         config.autoCreate, true);
31748         }else{
31749             this.el = dh.append(document.body,
31750                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31751         }
31752     }
31753     el = this.el;
31754     el.setDisplayed(true);
31755     el.hide = this.hideAction;
31756     this.id = el.id;
31757     el.addClass("x-dlg");
31758
31759     Roo.apply(this, config);
31760
31761     this.proxy = el.createProxy("x-dlg-proxy");
31762     this.proxy.hide = this.hideAction;
31763     this.proxy.setOpacity(.5);
31764     this.proxy.hide();
31765
31766     if(config.width){
31767         el.setWidth(config.width);
31768     }
31769     if(config.height){
31770         el.setHeight(config.height);
31771     }
31772     this.size = el.getSize();
31773     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31774         this.xy = [config.x,config.y];
31775     }else{
31776         this.xy = el.getCenterXY(true);
31777     }
31778     /** The header element @type Roo.Element */
31779     this.header = el.child("> .x-dlg-hd");
31780     /** The body element @type Roo.Element */
31781     this.body = el.child("> .x-dlg-bd");
31782     /** The footer element @type Roo.Element */
31783     this.footer = el.child("> .x-dlg-ft");
31784
31785     if(!this.header){
31786         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31787     }
31788     if(!this.body){
31789         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31790     }
31791
31792     this.header.unselectable();
31793     if(this.title){
31794         this.header.update(this.title);
31795     }
31796     // this element allows the dialog to be focused for keyboard event
31797     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31798     this.focusEl.swallowEvent("click", true);
31799
31800     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31801
31802     // wrap the body and footer for special rendering
31803     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31804     if(this.footer){
31805         this.bwrap.dom.appendChild(this.footer.dom);
31806     }
31807
31808     this.bg = this.el.createChild({
31809         tag: "div", cls:"x-dlg-bg",
31810         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31811     });
31812     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31813
31814
31815     if(this.autoScroll !== false && !this.autoTabs){
31816         this.body.setStyle("overflow", "auto");
31817     }
31818
31819     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31820
31821     if(this.closable !== false){
31822         this.el.addClass("x-dlg-closable");
31823         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31824         this.close.on("click", this.closeClick, this);
31825         this.close.addClassOnOver("x-dlg-close-over");
31826     }
31827     if(this.collapsible !== false){
31828         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31829         this.collapseBtn.on("click", this.collapseClick, this);
31830         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31831         this.header.on("dblclick", this.collapseClick, this);
31832     }
31833     if(this.resizable !== false){
31834         this.el.addClass("x-dlg-resizable");
31835         this.resizer = new Roo.Resizable(el, {
31836             minWidth: this.minWidth || 80,
31837             minHeight:this.minHeight || 80,
31838             handles: this.resizeHandles || "all",
31839             pinned: true
31840         });
31841         this.resizer.on("beforeresize", this.beforeResize, this);
31842         this.resizer.on("resize", this.onResize, this);
31843     }
31844     if(this.draggable !== false){
31845         el.addClass("x-dlg-draggable");
31846         if (!this.proxyDrag) {
31847             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31848         }
31849         else {
31850             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31851         }
31852         dd.setHandleElId(this.header.id);
31853         dd.endDrag = this.endMove.createDelegate(this);
31854         dd.startDrag = this.startMove.createDelegate(this);
31855         dd.onDrag = this.onDrag.createDelegate(this);
31856         dd.scroll = false;
31857         this.dd = dd;
31858     }
31859     if(this.modal){
31860         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31861         this.mask.enableDisplayMode("block");
31862         this.mask.hide();
31863         this.el.addClass("x-dlg-modal");
31864     }
31865     if(this.shadow){
31866         this.shadow = new Roo.Shadow({
31867             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31868             offset : this.shadowOffset
31869         });
31870     }else{
31871         this.shadowOffset = 0;
31872     }
31873     if(Roo.useShims && this.shim !== false){
31874         this.shim = this.el.createShim();
31875         this.shim.hide = this.hideAction;
31876         this.shim.hide();
31877     }else{
31878         this.shim = false;
31879     }
31880     if(this.autoTabs){
31881         this.initTabs();
31882     }
31883     if (this.buttons) { 
31884         var bts= this.buttons;
31885         this.buttons = [];
31886         Roo.each(bts, function(b) {
31887             this.addButton(b);
31888         }, this);
31889     }
31890     
31891     
31892     this.addEvents({
31893         /**
31894          * @event keydown
31895          * Fires when a key is pressed
31896          * @param {Roo.BasicDialog} this
31897          * @param {Roo.EventObject} e
31898          */
31899         "keydown" : true,
31900         /**
31901          * @event move
31902          * Fires when this dialog is moved by the user.
31903          * @param {Roo.BasicDialog} this
31904          * @param {Number} x The new page X
31905          * @param {Number} y The new page Y
31906          */
31907         "move" : true,
31908         /**
31909          * @event resize
31910          * Fires when this dialog is resized by the user.
31911          * @param {Roo.BasicDialog} this
31912          * @param {Number} width The new width
31913          * @param {Number} height The new height
31914          */
31915         "resize" : true,
31916         /**
31917          * @event beforehide
31918          * Fires before this dialog is hidden.
31919          * @param {Roo.BasicDialog} this
31920          */
31921         "beforehide" : true,
31922         /**
31923          * @event hide
31924          * Fires when this dialog is hidden.
31925          * @param {Roo.BasicDialog} this
31926          */
31927         "hide" : true,
31928         /**
31929          * @event beforeshow
31930          * Fires before this dialog is shown.
31931          * @param {Roo.BasicDialog} this
31932          */
31933         "beforeshow" : true,
31934         /**
31935          * @event show
31936          * Fires when this dialog is shown.
31937          * @param {Roo.BasicDialog} this
31938          */
31939         "show" : true
31940     });
31941     el.on("keydown", this.onKeyDown, this);
31942     el.on("mousedown", this.toFront, this);
31943     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31944     this.el.hide();
31945     Roo.DialogManager.register(this);
31946     Roo.BasicDialog.superclass.constructor.call(this);
31947 };
31948
31949 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31950     shadowOffset: Roo.isIE ? 6 : 5,
31951     minHeight: 80,
31952     minWidth: 200,
31953     minButtonWidth: 75,
31954     defaultButton: null,
31955     buttonAlign: "right",
31956     tabTag: 'div',
31957     firstShow: true,
31958
31959     /**
31960      * Sets the dialog title text
31961      * @param {String} text The title text to display
31962      * @return {Roo.BasicDialog} this
31963      */
31964     setTitle : function(text){
31965         this.header.update(text);
31966         return this;
31967     },
31968
31969     // private
31970     closeClick : function(){
31971         this.hide();
31972     },
31973
31974     // private
31975     collapseClick : function(){
31976         this[this.collapsed ? "expand" : "collapse"]();
31977     },
31978
31979     /**
31980      * Collapses the dialog to its minimized state (only the title bar is visible).
31981      * Equivalent to the user clicking the collapse dialog button.
31982      */
31983     collapse : function(){
31984         if(!this.collapsed){
31985             this.collapsed = true;
31986             this.el.addClass("x-dlg-collapsed");
31987             this.restoreHeight = this.el.getHeight();
31988             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31989         }
31990     },
31991
31992     /**
31993      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31994      * clicking the expand dialog button.
31995      */
31996     expand : function(){
31997         if(this.collapsed){
31998             this.collapsed = false;
31999             this.el.removeClass("x-dlg-collapsed");
32000             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32001         }
32002     },
32003
32004     /**
32005      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32006      * @return {Roo.TabPanel} The tabs component
32007      */
32008     initTabs : function(){
32009         var tabs = this.getTabs();
32010         while(tabs.getTab(0)){
32011             tabs.removeTab(0);
32012         }
32013         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32014             var dom = el.dom;
32015             tabs.addTab(Roo.id(dom), dom.title);
32016             dom.title = "";
32017         });
32018         tabs.activate(0);
32019         return tabs;
32020     },
32021
32022     // private
32023     beforeResize : function(){
32024         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32025     },
32026
32027     // private
32028     onResize : function(){
32029         this.refreshSize();
32030         this.syncBodyHeight();
32031         this.adjustAssets();
32032         this.focus();
32033         this.fireEvent("resize", this, this.size.width, this.size.height);
32034     },
32035
32036     // private
32037     onKeyDown : function(e){
32038         if(this.isVisible()){
32039             this.fireEvent("keydown", this, e);
32040         }
32041     },
32042
32043     /**
32044      * Resizes the dialog.
32045      * @param {Number} width
32046      * @param {Number} height
32047      * @return {Roo.BasicDialog} this
32048      */
32049     resizeTo : function(width, height){
32050         this.el.setSize(width, height);
32051         this.size = {width: width, height: height};
32052         this.syncBodyHeight();
32053         if(this.fixedcenter){
32054             this.center();
32055         }
32056         if(this.isVisible()){
32057             this.constrainXY();
32058             this.adjustAssets();
32059         }
32060         this.fireEvent("resize", this, width, height);
32061         return this;
32062     },
32063
32064
32065     /**
32066      * Resizes the dialog to fit the specified content size.
32067      * @param {Number} width
32068      * @param {Number} height
32069      * @return {Roo.BasicDialog} this
32070      */
32071     setContentSize : function(w, h){
32072         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32073         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32074         //if(!this.el.isBorderBox()){
32075             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32076             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32077         //}
32078         if(this.tabs){
32079             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32080             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32081         }
32082         this.resizeTo(w, h);
32083         return this;
32084     },
32085
32086     /**
32087      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32088      * executed in response to a particular key being pressed while the dialog is active.
32089      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32090      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32091      * @param {Function} fn The function to call
32092      * @param {Object} scope (optional) The scope of the function
32093      * @return {Roo.BasicDialog} this
32094      */
32095     addKeyListener : function(key, fn, scope){
32096         var keyCode, shift, ctrl, alt;
32097         if(typeof key == "object" && !(key instanceof Array)){
32098             keyCode = key["key"];
32099             shift = key["shift"];
32100             ctrl = key["ctrl"];
32101             alt = key["alt"];
32102         }else{
32103             keyCode = key;
32104         }
32105         var handler = function(dlg, e){
32106             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32107                 var k = e.getKey();
32108                 if(keyCode instanceof Array){
32109                     for(var i = 0, len = keyCode.length; i < len; i++){
32110                         if(keyCode[i] == k){
32111                           fn.call(scope || window, dlg, k, e);
32112                           return;
32113                         }
32114                     }
32115                 }else{
32116                     if(k == keyCode){
32117                         fn.call(scope || window, dlg, k, e);
32118                     }
32119                 }
32120             }
32121         };
32122         this.on("keydown", handler);
32123         return this;
32124     },
32125
32126     /**
32127      * Returns the TabPanel component (creates it if it doesn't exist).
32128      * Note: If you wish to simply check for the existence of tabs without creating them,
32129      * check for a null 'tabs' property.
32130      * @return {Roo.TabPanel} The tabs component
32131      */
32132     getTabs : function(){
32133         if(!this.tabs){
32134             this.el.addClass("x-dlg-auto-tabs");
32135             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32136             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32137         }
32138         return this.tabs;
32139     },
32140
32141     /**
32142      * Adds a button to the footer section of the dialog.
32143      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32144      * object or a valid Roo.DomHelper element config
32145      * @param {Function} handler The function called when the button is clicked
32146      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32147      * @return {Roo.Button} The new button
32148      */
32149     addButton : function(config, handler, scope){
32150         var dh = Roo.DomHelper;
32151         if(!this.footer){
32152             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32153         }
32154         if(!this.btnContainer){
32155             var tb = this.footer.createChild({
32156
32157                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32158                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32159             }, null, true);
32160             this.btnContainer = tb.firstChild.firstChild.firstChild;
32161         }
32162         var bconfig = {
32163             handler: handler,
32164             scope: scope,
32165             minWidth: this.minButtonWidth,
32166             hideParent:true
32167         };
32168         if(typeof config == "string"){
32169             bconfig.text = config;
32170         }else{
32171             if(config.tag){
32172                 bconfig.dhconfig = config;
32173             }else{
32174                 Roo.apply(bconfig, config);
32175             }
32176         }
32177         var fc = false;
32178         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32179             bconfig.position = Math.max(0, bconfig.position);
32180             fc = this.btnContainer.childNodes[bconfig.position];
32181         }
32182          
32183         var btn = new Roo.Button(
32184             fc ? 
32185                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32186                 : this.btnContainer.appendChild(document.createElement("td")),
32187             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32188             bconfig
32189         );
32190         this.syncBodyHeight();
32191         if(!this.buttons){
32192             /**
32193              * Array of all the buttons that have been added to this dialog via addButton
32194              * @type Array
32195              */
32196             this.buttons = [];
32197         }
32198         this.buttons.push(btn);
32199         return btn;
32200     },
32201
32202     /**
32203      * Sets the default button to be focused when the dialog is displayed.
32204      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32205      * @return {Roo.BasicDialog} this
32206      */
32207     setDefaultButton : function(btn){
32208         this.defaultButton = btn;
32209         return this;
32210     },
32211
32212     // private
32213     getHeaderFooterHeight : function(safe){
32214         var height = 0;
32215         if(this.header){
32216            height += this.header.getHeight();
32217         }
32218         if(this.footer){
32219            var fm = this.footer.getMargins();
32220             height += (this.footer.getHeight()+fm.top+fm.bottom);
32221         }
32222         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32223         height += this.centerBg.getPadding("tb");
32224         return height;
32225     },
32226
32227     // private
32228     syncBodyHeight : function()
32229     {
32230         var bd = this.body, // the text
32231             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32232             bw = this.bwrap;
32233         var height = this.size.height - this.getHeaderFooterHeight(false);
32234         bd.setHeight(height-bd.getMargins("tb"));
32235         var hh = this.header.getHeight();
32236         var h = this.size.height-hh;
32237         cb.setHeight(h);
32238         
32239         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32240         bw.setHeight(h-cb.getPadding("tb"));
32241         
32242         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32243         bd.setWidth(bw.getWidth(true));
32244         if(this.tabs){
32245             this.tabs.syncHeight();
32246             if(Roo.isIE){
32247                 this.tabs.el.repaint();
32248             }
32249         }
32250     },
32251
32252     /**
32253      * Restores the previous state of the dialog if Roo.state is configured.
32254      * @return {Roo.BasicDialog} this
32255      */
32256     restoreState : function(){
32257         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32258         if(box && box.width){
32259             this.xy = [box.x, box.y];
32260             this.resizeTo(box.width, box.height);
32261         }
32262         return this;
32263     },
32264
32265     // private
32266     beforeShow : function(){
32267         this.expand();
32268         if(this.fixedcenter){
32269             this.xy = this.el.getCenterXY(true);
32270         }
32271         if(this.modal){
32272             Roo.get(document.body).addClass("x-body-masked");
32273             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32274             this.mask.show();
32275         }
32276         this.constrainXY();
32277     },
32278
32279     // private
32280     animShow : function(){
32281         var b = Roo.get(this.animateTarget).getBox();
32282         this.proxy.setSize(b.width, b.height);
32283         this.proxy.setLocation(b.x, b.y);
32284         this.proxy.show();
32285         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32286                     true, .35, this.showEl.createDelegate(this));
32287     },
32288
32289     /**
32290      * Shows the dialog.
32291      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32292      * @return {Roo.BasicDialog} this
32293      */
32294     show : function(animateTarget){
32295         if (this.fireEvent("beforeshow", this) === false){
32296             return;
32297         }
32298         if(this.syncHeightBeforeShow){
32299             this.syncBodyHeight();
32300         }else if(this.firstShow){
32301             this.firstShow = false;
32302             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32303         }
32304         this.animateTarget = animateTarget || this.animateTarget;
32305         if(!this.el.isVisible()){
32306             this.beforeShow();
32307             if(this.animateTarget && Roo.get(this.animateTarget)){
32308                 this.animShow();
32309             }else{
32310                 this.showEl();
32311             }
32312         }
32313         return this;
32314     },
32315
32316     // private
32317     showEl : function(){
32318         this.proxy.hide();
32319         this.el.setXY(this.xy);
32320         this.el.show();
32321         this.adjustAssets(true);
32322         this.toFront();
32323         this.focus();
32324         // IE peekaboo bug - fix found by Dave Fenwick
32325         if(Roo.isIE){
32326             this.el.repaint();
32327         }
32328         this.fireEvent("show", this);
32329     },
32330
32331     /**
32332      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32333      * dialog itself will receive focus.
32334      */
32335     focus : function(){
32336         if(this.defaultButton){
32337             this.defaultButton.focus();
32338         }else{
32339             this.focusEl.focus();
32340         }
32341     },
32342
32343     // private
32344     constrainXY : function(){
32345         if(this.constraintoviewport !== false){
32346             if(!this.viewSize){
32347                 if(this.container){
32348                     var s = this.container.getSize();
32349                     this.viewSize = [s.width, s.height];
32350                 }else{
32351                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32352                 }
32353             }
32354             var s = Roo.get(this.container||document).getScroll();
32355
32356             var x = this.xy[0], y = this.xy[1];
32357             var w = this.size.width, h = this.size.height;
32358             var vw = this.viewSize[0], vh = this.viewSize[1];
32359             // only move it if it needs it
32360             var moved = false;
32361             // first validate right/bottom
32362             if(x + w > vw+s.left){
32363                 x = vw - w;
32364                 moved = true;
32365             }
32366             if(y + h > vh+s.top){
32367                 y = vh - h;
32368                 moved = true;
32369             }
32370             // then make sure top/left isn't negative
32371             if(x < s.left){
32372                 x = s.left;
32373                 moved = true;
32374             }
32375             if(y < s.top){
32376                 y = s.top;
32377                 moved = true;
32378             }
32379             if(moved){
32380                 // cache xy
32381                 this.xy = [x, y];
32382                 if(this.isVisible()){
32383                     this.el.setLocation(x, y);
32384                     this.adjustAssets();
32385                 }
32386             }
32387         }
32388     },
32389
32390     // private
32391     onDrag : function(){
32392         if(!this.proxyDrag){
32393             this.xy = this.el.getXY();
32394             this.adjustAssets();
32395         }
32396     },
32397
32398     // private
32399     adjustAssets : function(doShow){
32400         var x = this.xy[0], y = this.xy[1];
32401         var w = this.size.width, h = this.size.height;
32402         if(doShow === true){
32403             if(this.shadow){
32404                 this.shadow.show(this.el);
32405             }
32406             if(this.shim){
32407                 this.shim.show();
32408             }
32409         }
32410         if(this.shadow && this.shadow.isVisible()){
32411             this.shadow.show(this.el);
32412         }
32413         if(this.shim && this.shim.isVisible()){
32414             this.shim.setBounds(x, y, w, h);
32415         }
32416     },
32417
32418     // private
32419     adjustViewport : function(w, h){
32420         if(!w || !h){
32421             w = Roo.lib.Dom.getViewWidth();
32422             h = Roo.lib.Dom.getViewHeight();
32423         }
32424         // cache the size
32425         this.viewSize = [w, h];
32426         if(this.modal && this.mask.isVisible()){
32427             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32428             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32429         }
32430         if(this.isVisible()){
32431             this.constrainXY();
32432         }
32433     },
32434
32435     /**
32436      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32437      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32438      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32439      */
32440     destroy : function(removeEl){
32441         if(this.isVisible()){
32442             this.animateTarget = null;
32443             this.hide();
32444         }
32445         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32446         if(this.tabs){
32447             this.tabs.destroy(removeEl);
32448         }
32449         Roo.destroy(
32450              this.shim,
32451              this.proxy,
32452              this.resizer,
32453              this.close,
32454              this.mask
32455         );
32456         if(this.dd){
32457             this.dd.unreg();
32458         }
32459         if(this.buttons){
32460            for(var i = 0, len = this.buttons.length; i < len; i++){
32461                this.buttons[i].destroy();
32462            }
32463         }
32464         this.el.removeAllListeners();
32465         if(removeEl === true){
32466             this.el.update("");
32467             this.el.remove();
32468         }
32469         Roo.DialogManager.unregister(this);
32470     },
32471
32472     // private
32473     startMove : function(){
32474         if(this.proxyDrag){
32475             this.proxy.show();
32476         }
32477         if(this.constraintoviewport !== false){
32478             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32479         }
32480     },
32481
32482     // private
32483     endMove : function(){
32484         if(!this.proxyDrag){
32485             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32486         }else{
32487             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32488             this.proxy.hide();
32489         }
32490         this.refreshSize();
32491         this.adjustAssets();
32492         this.focus();
32493         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32494     },
32495
32496     /**
32497      * Brings this dialog to the front of any other visible dialogs
32498      * @return {Roo.BasicDialog} this
32499      */
32500     toFront : function(){
32501         Roo.DialogManager.bringToFront(this);
32502         return this;
32503     },
32504
32505     /**
32506      * Sends this dialog to the back (under) of any other visible dialogs
32507      * @return {Roo.BasicDialog} this
32508      */
32509     toBack : function(){
32510         Roo.DialogManager.sendToBack(this);
32511         return this;
32512     },
32513
32514     /**
32515      * Centers this dialog in the viewport
32516      * @return {Roo.BasicDialog} this
32517      */
32518     center : function(){
32519         var xy = this.el.getCenterXY(true);
32520         this.moveTo(xy[0], xy[1]);
32521         return this;
32522     },
32523
32524     /**
32525      * Moves the dialog's top-left corner to the specified point
32526      * @param {Number} x
32527      * @param {Number} y
32528      * @return {Roo.BasicDialog} this
32529      */
32530     moveTo : function(x, y){
32531         this.xy = [x,y];
32532         if(this.isVisible()){
32533             this.el.setXY(this.xy);
32534             this.adjustAssets();
32535         }
32536         return this;
32537     },
32538
32539     /**
32540      * Aligns the dialog to the specified element
32541      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32542      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32543      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32544      * @return {Roo.BasicDialog} this
32545      */
32546     alignTo : function(element, position, offsets){
32547         this.xy = this.el.getAlignToXY(element, position, offsets);
32548         if(this.isVisible()){
32549             this.el.setXY(this.xy);
32550             this.adjustAssets();
32551         }
32552         return this;
32553     },
32554
32555     /**
32556      * Anchors an element to another element and realigns it when the window is resized.
32557      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32558      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32559      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32560      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32561      * is a number, it is used as the buffer delay (defaults to 50ms).
32562      * @return {Roo.BasicDialog} this
32563      */
32564     anchorTo : function(el, alignment, offsets, monitorScroll){
32565         var action = function(){
32566             this.alignTo(el, alignment, offsets);
32567         };
32568         Roo.EventManager.onWindowResize(action, this);
32569         var tm = typeof monitorScroll;
32570         if(tm != 'undefined'){
32571             Roo.EventManager.on(window, 'scroll', action, this,
32572                 {buffer: tm == 'number' ? monitorScroll : 50});
32573         }
32574         action.call(this);
32575         return this;
32576     },
32577
32578     /**
32579      * Returns true if the dialog is visible
32580      * @return {Boolean}
32581      */
32582     isVisible : function(){
32583         return this.el.isVisible();
32584     },
32585
32586     // private
32587     animHide : function(callback){
32588         var b = Roo.get(this.animateTarget).getBox();
32589         this.proxy.show();
32590         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32591         this.el.hide();
32592         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32593                     this.hideEl.createDelegate(this, [callback]));
32594     },
32595
32596     /**
32597      * Hides the dialog.
32598      * @param {Function} callback (optional) Function to call when the dialog is hidden
32599      * @return {Roo.BasicDialog} this
32600      */
32601     hide : function(callback){
32602         if (this.fireEvent("beforehide", this) === false){
32603             return;
32604         }
32605         if(this.shadow){
32606             this.shadow.hide();
32607         }
32608         if(this.shim) {
32609           this.shim.hide();
32610         }
32611         // sometimes animateTarget seems to get set.. causing problems...
32612         // this just double checks..
32613         if(this.animateTarget && Roo.get(this.animateTarget)) {
32614            this.animHide(callback);
32615         }else{
32616             this.el.hide();
32617             this.hideEl(callback);
32618         }
32619         return this;
32620     },
32621
32622     // private
32623     hideEl : function(callback){
32624         this.proxy.hide();
32625         if(this.modal){
32626             this.mask.hide();
32627             Roo.get(document.body).removeClass("x-body-masked");
32628         }
32629         this.fireEvent("hide", this);
32630         if(typeof callback == "function"){
32631             callback();
32632         }
32633     },
32634
32635     // private
32636     hideAction : function(){
32637         this.setLeft("-10000px");
32638         this.setTop("-10000px");
32639         this.setStyle("visibility", "hidden");
32640     },
32641
32642     // private
32643     refreshSize : function(){
32644         this.size = this.el.getSize();
32645         this.xy = this.el.getXY();
32646         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32647     },
32648
32649     // private
32650     // z-index is managed by the DialogManager and may be overwritten at any time
32651     setZIndex : function(index){
32652         if(this.modal){
32653             this.mask.setStyle("z-index", index);
32654         }
32655         if(this.shim){
32656             this.shim.setStyle("z-index", ++index);
32657         }
32658         if(this.shadow){
32659             this.shadow.setZIndex(++index);
32660         }
32661         this.el.setStyle("z-index", ++index);
32662         if(this.proxy){
32663             this.proxy.setStyle("z-index", ++index);
32664         }
32665         if(this.resizer){
32666             this.resizer.proxy.setStyle("z-index", ++index);
32667         }
32668
32669         this.lastZIndex = index;
32670     },
32671
32672     /**
32673      * Returns the element for this dialog
32674      * @return {Roo.Element} The underlying dialog Element
32675      */
32676     getEl : function(){
32677         return this.el;
32678     }
32679 });
32680
32681 /**
32682  * @class Roo.DialogManager
32683  * Provides global access to BasicDialogs that have been created and
32684  * support for z-indexing (layering) multiple open dialogs.
32685  */
32686 Roo.DialogManager = function(){
32687     var list = {};
32688     var accessList = [];
32689     var front = null;
32690
32691     // private
32692     var sortDialogs = function(d1, d2){
32693         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32694     };
32695
32696     // private
32697     var orderDialogs = function(){
32698         accessList.sort(sortDialogs);
32699         var seed = Roo.DialogManager.zseed;
32700         for(var i = 0, len = accessList.length; i < len; i++){
32701             var dlg = accessList[i];
32702             if(dlg){
32703                 dlg.setZIndex(seed + (i*10));
32704             }
32705         }
32706     };
32707
32708     return {
32709         /**
32710          * The starting z-index for BasicDialogs (defaults to 9000)
32711          * @type Number The z-index value
32712          */
32713         zseed : 9000,
32714
32715         // private
32716         register : function(dlg){
32717             list[dlg.id] = dlg;
32718             accessList.push(dlg);
32719         },
32720
32721         // private
32722         unregister : function(dlg){
32723             delete list[dlg.id];
32724             var i=0;
32725             var len=0;
32726             if(!accessList.indexOf){
32727                 for(  i = 0, len = accessList.length; i < len; i++){
32728                     if(accessList[i] == dlg){
32729                         accessList.splice(i, 1);
32730                         return;
32731                     }
32732                 }
32733             }else{
32734                  i = accessList.indexOf(dlg);
32735                 if(i != -1){
32736                     accessList.splice(i, 1);
32737                 }
32738             }
32739         },
32740
32741         /**
32742          * Gets a registered dialog by id
32743          * @param {String/Object} id The id of the dialog or a dialog
32744          * @return {Roo.BasicDialog} this
32745          */
32746         get : function(id){
32747             return typeof id == "object" ? id : list[id];
32748         },
32749
32750         /**
32751          * Brings the specified dialog to the front
32752          * @param {String/Object} dlg The id of the dialog or a dialog
32753          * @return {Roo.BasicDialog} this
32754          */
32755         bringToFront : function(dlg){
32756             dlg = this.get(dlg);
32757             if(dlg != front){
32758                 front = dlg;
32759                 dlg._lastAccess = new Date().getTime();
32760                 orderDialogs();
32761             }
32762             return dlg;
32763         },
32764
32765         /**
32766          * Sends the specified dialog to the back
32767          * @param {String/Object} dlg The id of the dialog or a dialog
32768          * @return {Roo.BasicDialog} this
32769          */
32770         sendToBack : function(dlg){
32771             dlg = this.get(dlg);
32772             dlg._lastAccess = -(new Date().getTime());
32773             orderDialogs();
32774             return dlg;
32775         },
32776
32777         /**
32778          * Hides all dialogs
32779          */
32780         hideAll : function(){
32781             for(var id in list){
32782                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32783                     list[id].hide();
32784                 }
32785             }
32786         }
32787     };
32788 }();
32789
32790 /**
32791  * @class Roo.LayoutDialog
32792  * @extends Roo.BasicDialog
32793  * Dialog which provides adjustments for working with a layout in a Dialog.
32794  * Add your necessary layout config options to the dialog's config.<br>
32795  * Example usage (including a nested layout):
32796  * <pre><code>
32797 if(!dialog){
32798     dialog = new Roo.LayoutDialog("download-dlg", {
32799         modal: true,
32800         width:600,
32801         height:450,
32802         shadow:true,
32803         minWidth:500,
32804         minHeight:350,
32805         autoTabs:true,
32806         proxyDrag:true,
32807         // layout config merges with the dialog config
32808         center:{
32809             tabPosition: "top",
32810             alwaysShowTabs: true
32811         }
32812     });
32813     dialog.addKeyListener(27, dialog.hide, dialog);
32814     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32815     dialog.addButton("Build It!", this.getDownload, this);
32816
32817     // we can even add nested layouts
32818     var innerLayout = new Roo.BorderLayout("dl-inner", {
32819         east: {
32820             initialSize: 200,
32821             autoScroll:true,
32822             split:true
32823         },
32824         center: {
32825             autoScroll:true
32826         }
32827     });
32828     innerLayout.beginUpdate();
32829     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32830     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32831     innerLayout.endUpdate(true);
32832
32833     var layout = dialog.getLayout();
32834     layout.beginUpdate();
32835     layout.add("center", new Roo.ContentPanel("standard-panel",
32836                         {title: "Download the Source", fitToFrame:true}));
32837     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32838                {title: "Build your own roo.js"}));
32839     layout.getRegion("center").showPanel(sp);
32840     layout.endUpdate();
32841 }
32842 </code></pre>
32843     * @constructor
32844     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32845     * @param {Object} config configuration options
32846   */
32847 Roo.LayoutDialog = function(el, cfg){
32848     
32849     var config=  cfg;
32850     if (typeof(cfg) == 'undefined') {
32851         config = Roo.apply({}, el);
32852         // not sure why we use documentElement here.. - it should always be body.
32853         // IE7 borks horribly if we use documentElement.
32854         // webkit also does not like documentElement - it creates a body element...
32855         el = Roo.get( document.body || document.documentElement ).createChild();
32856         //config.autoCreate = true;
32857     }
32858     
32859     
32860     config.autoTabs = false;
32861     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32862     this.body.setStyle({overflow:"hidden", position:"relative"});
32863     this.layout = new Roo.BorderLayout(this.body.dom, config);
32864     this.layout.monitorWindowResize = false;
32865     this.el.addClass("x-dlg-auto-layout");
32866     // fix case when center region overwrites center function
32867     this.center = Roo.BasicDialog.prototype.center;
32868     this.on("show", this.layout.layout, this.layout, true);
32869     if (config.items) {
32870         var xitems = config.items;
32871         delete config.items;
32872         Roo.each(xitems, this.addxtype, this);
32873     }
32874     
32875     
32876 };
32877 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32878     /**
32879      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32880      * @deprecated
32881      */
32882     endUpdate : function(){
32883         this.layout.endUpdate();
32884     },
32885
32886     /**
32887      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32888      *  @deprecated
32889      */
32890     beginUpdate : function(){
32891         this.layout.beginUpdate();
32892     },
32893
32894     /**
32895      * Get the BorderLayout for this dialog
32896      * @return {Roo.BorderLayout}
32897      */
32898     getLayout : function(){
32899         return this.layout;
32900     },
32901
32902     showEl : function(){
32903         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32904         if(Roo.isIE7){
32905             this.layout.layout();
32906         }
32907     },
32908
32909     // private
32910     // Use the syncHeightBeforeShow config option to control this automatically
32911     syncBodyHeight : function(){
32912         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32913         if(this.layout){this.layout.layout();}
32914     },
32915     
32916       /**
32917      * Add an xtype element (actually adds to the layout.)
32918      * @return {Object} xdata xtype object data.
32919      */
32920     
32921     addxtype : function(c) {
32922         return this.layout.addxtype(c);
32923     }
32924 });/*
32925  * Based on:
32926  * Ext JS Library 1.1.1
32927  * Copyright(c) 2006-2007, Ext JS, LLC.
32928  *
32929  * Originally Released Under LGPL - original licence link has changed is not relivant.
32930  *
32931  * Fork - LGPL
32932  * <script type="text/javascript">
32933  */
32934  
32935 /**
32936  * @class Roo.MessageBox
32937  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32938  * Example usage:
32939  *<pre><code>
32940 // Basic alert:
32941 Roo.Msg.alert('Status', 'Changes saved successfully.');
32942
32943 // Prompt for user data:
32944 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32945     if (btn == 'ok'){
32946         // process text value...
32947     }
32948 });
32949
32950 // Show a dialog using config options:
32951 Roo.Msg.show({
32952    title:'Save Changes?',
32953    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32954    buttons: Roo.Msg.YESNOCANCEL,
32955    fn: processResult,
32956    animEl: 'elId'
32957 });
32958 </code></pre>
32959  * @singleton
32960  */
32961 Roo.MessageBox = function(){
32962     var dlg, opt, mask, waitTimer;
32963     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32964     var buttons, activeTextEl, bwidth;
32965
32966     // private
32967     var handleButton = function(button){
32968         dlg.hide();
32969         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32970     };
32971
32972     // private
32973     var handleHide = function(){
32974         if(opt && opt.cls){
32975             dlg.el.removeClass(opt.cls);
32976         }
32977         if(waitTimer){
32978             Roo.TaskMgr.stop(waitTimer);
32979             waitTimer = null;
32980         }
32981     };
32982
32983     // private
32984     var updateButtons = function(b){
32985         var width = 0;
32986         if(!b){
32987             buttons["ok"].hide();
32988             buttons["cancel"].hide();
32989             buttons["yes"].hide();
32990             buttons["no"].hide();
32991             dlg.footer.dom.style.display = 'none';
32992             return width;
32993         }
32994         dlg.footer.dom.style.display = '';
32995         for(var k in buttons){
32996             if(typeof buttons[k] != "function"){
32997                 if(b[k]){
32998                     buttons[k].show();
32999                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33000                     width += buttons[k].el.getWidth()+15;
33001                 }else{
33002                     buttons[k].hide();
33003                 }
33004             }
33005         }
33006         return width;
33007     };
33008
33009     // private
33010     var handleEsc = function(d, k, e){
33011         if(opt && opt.closable !== false){
33012             dlg.hide();
33013         }
33014         if(e){
33015             e.stopEvent();
33016         }
33017     };
33018
33019     return {
33020         /**
33021          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33022          * @return {Roo.BasicDialog} The BasicDialog element
33023          */
33024         getDialog : function(){
33025            if(!dlg){
33026                 dlg = new Roo.BasicDialog("x-msg-box", {
33027                     autoCreate : true,
33028                     shadow: true,
33029                     draggable: true,
33030                     resizable:false,
33031                     constraintoviewport:false,
33032                     fixedcenter:true,
33033                     collapsible : false,
33034                     shim:true,
33035                     modal: true,
33036                     width:400, height:100,
33037                     buttonAlign:"center",
33038                     closeClick : function(){
33039                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33040                             handleButton("no");
33041                         }else{
33042                             handleButton("cancel");
33043                         }
33044                     }
33045                 });
33046                 dlg.on("hide", handleHide);
33047                 mask = dlg.mask;
33048                 dlg.addKeyListener(27, handleEsc);
33049                 buttons = {};
33050                 var bt = this.buttonText;
33051                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33052                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33053                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33054                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33055                 bodyEl = dlg.body.createChild({
33056
33057                     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>'
33058                 });
33059                 msgEl = bodyEl.dom.firstChild;
33060                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33061                 textboxEl.enableDisplayMode();
33062                 textboxEl.addKeyListener([10,13], function(){
33063                     if(dlg.isVisible() && opt && opt.buttons){
33064                         if(opt.buttons.ok){
33065                             handleButton("ok");
33066                         }else if(opt.buttons.yes){
33067                             handleButton("yes");
33068                         }
33069                     }
33070                 });
33071                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33072                 textareaEl.enableDisplayMode();
33073                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33074                 progressEl.enableDisplayMode();
33075                 var pf = progressEl.dom.firstChild;
33076                 if (pf) {
33077                     pp = Roo.get(pf.firstChild);
33078                     pp.setHeight(pf.offsetHeight);
33079                 }
33080                 
33081             }
33082             return dlg;
33083         },
33084
33085         /**
33086          * Updates the message box body text
33087          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33088          * the XHTML-compliant non-breaking space character '&amp;#160;')
33089          * @return {Roo.MessageBox} This message box
33090          */
33091         updateText : function(text){
33092             if(!dlg.isVisible() && !opt.width){
33093                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33094             }
33095             msgEl.innerHTML = text || '&#160;';
33096       
33097             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33098             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33099             var w = Math.max(
33100                     Math.min(opt.width || cw , this.maxWidth), 
33101                     Math.max(opt.minWidth || this.minWidth, bwidth)
33102             );
33103             if(opt.prompt){
33104                 activeTextEl.setWidth(w);
33105             }
33106             if(dlg.isVisible()){
33107                 dlg.fixedcenter = false;
33108             }
33109             // to big, make it scroll. = But as usual stupid IE does not support
33110             // !important..
33111             
33112             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33113                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33114                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33115             } else {
33116                 bodyEl.dom.style.height = '';
33117                 bodyEl.dom.style.overflowY = '';
33118             }
33119             if (cw > w) {
33120                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33121             } else {
33122                 bodyEl.dom.style.overflowX = '';
33123             }
33124             
33125             dlg.setContentSize(w, bodyEl.getHeight());
33126             if(dlg.isVisible()){
33127                 dlg.fixedcenter = true;
33128             }
33129             return this;
33130         },
33131
33132         /**
33133          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33134          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33135          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33136          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33137          * @return {Roo.MessageBox} This message box
33138          */
33139         updateProgress : function(value, text){
33140             if(text){
33141                 this.updateText(text);
33142             }
33143             if (pp) { // weird bug on my firefox - for some reason this is not defined
33144                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33145             }
33146             return this;
33147         },        
33148
33149         /**
33150          * Returns true if the message box is currently displayed
33151          * @return {Boolean} True if the message box is visible, else false
33152          */
33153         isVisible : function(){
33154             return dlg && dlg.isVisible();  
33155         },
33156
33157         /**
33158          * Hides the message box if it is displayed
33159          */
33160         hide : function(){
33161             if(this.isVisible()){
33162                 dlg.hide();
33163             }  
33164         },
33165
33166         /**
33167          * Displays a new message box, or reinitializes an existing message box, based on the config options
33168          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33169          * The following config object properties are supported:
33170          * <pre>
33171 Property    Type             Description
33172 ----------  ---------------  ------------------------------------------------------------------------------------
33173 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33174                                    closes (defaults to undefined)
33175 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33176                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33177 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33178                                    progress and wait dialogs will ignore this property and always hide the
33179                                    close button as they can only be closed programmatically.
33180 cls               String           A custom CSS class to apply to the message box element
33181 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33182                                    displayed (defaults to 75)
33183 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33184                                    function will be btn (the name of the button that was clicked, if applicable,
33185                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33186                                    Progress and wait dialogs will ignore this option since they do not respond to
33187                                    user actions and can only be closed programmatically, so any required function
33188                                    should be called by the same code after it closes the dialog.
33189 icon              String           A CSS class that provides a background image to be used as an icon for
33190                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33191 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33192 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33193 modal             Boolean          False to allow user interaction with the page while the message box is
33194                                    displayed (defaults to true)
33195 msg               String           A string that will replace the existing message box body text (defaults
33196                                    to the XHTML-compliant non-breaking space character '&#160;')
33197 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33198 progress          Boolean          True to display a progress bar (defaults to false)
33199 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33200 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33201 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33202 title             String           The title text
33203 value             String           The string value to set into the active textbox element if displayed
33204 wait              Boolean          True to display a progress bar (defaults to false)
33205 width             Number           The width of the dialog in pixels
33206 </pre>
33207          *
33208          * Example usage:
33209          * <pre><code>
33210 Roo.Msg.show({
33211    title: 'Address',
33212    msg: 'Please enter your address:',
33213    width: 300,
33214    buttons: Roo.MessageBox.OKCANCEL,
33215    multiline: true,
33216    fn: saveAddress,
33217    animEl: 'addAddressBtn'
33218 });
33219 </code></pre>
33220          * @param {Object} config Configuration options
33221          * @return {Roo.MessageBox} This message box
33222          */
33223         show : function(options)
33224         {
33225             
33226             // this causes nightmares if you show one dialog after another
33227             // especially on callbacks..
33228              
33229             if(this.isVisible()){
33230                 
33231                 this.hide();
33232                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33233                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33234                 Roo.log("New Dialog Message:" +  options.msg )
33235                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33236                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33237                 
33238             }
33239             var d = this.getDialog();
33240             opt = options;
33241             d.setTitle(opt.title || "&#160;");
33242             d.close.setDisplayed(opt.closable !== false);
33243             activeTextEl = textboxEl;
33244             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33245             if(opt.prompt){
33246                 if(opt.multiline){
33247                     textboxEl.hide();
33248                     textareaEl.show();
33249                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33250                         opt.multiline : this.defaultTextHeight);
33251                     activeTextEl = textareaEl;
33252                 }else{
33253                     textboxEl.show();
33254                     textareaEl.hide();
33255                 }
33256             }else{
33257                 textboxEl.hide();
33258                 textareaEl.hide();
33259             }
33260             progressEl.setDisplayed(opt.progress === true);
33261             this.updateProgress(0);
33262             activeTextEl.dom.value = opt.value || "";
33263             if(opt.prompt){
33264                 dlg.setDefaultButton(activeTextEl);
33265             }else{
33266                 var bs = opt.buttons;
33267                 var db = null;
33268                 if(bs && bs.ok){
33269                     db = buttons["ok"];
33270                 }else if(bs && bs.yes){
33271                     db = buttons["yes"];
33272                 }
33273                 dlg.setDefaultButton(db);
33274             }
33275             bwidth = updateButtons(opt.buttons);
33276             this.updateText(opt.msg);
33277             if(opt.cls){
33278                 d.el.addClass(opt.cls);
33279             }
33280             d.proxyDrag = opt.proxyDrag === true;
33281             d.modal = opt.modal !== false;
33282             d.mask = opt.modal !== false ? mask : false;
33283             if(!d.isVisible()){
33284                 // force it to the end of the z-index stack so it gets a cursor in FF
33285                 document.body.appendChild(dlg.el.dom);
33286                 d.animateTarget = null;
33287                 d.show(options.animEl);
33288             }
33289             return this;
33290         },
33291
33292         /**
33293          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33294          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33295          * and closing the message box when the process is complete.
33296          * @param {String} title The title bar text
33297          * @param {String} msg The message box body text
33298          * @return {Roo.MessageBox} This message box
33299          */
33300         progress : function(title, msg){
33301             this.show({
33302                 title : title,
33303                 msg : msg,
33304                 buttons: false,
33305                 progress:true,
33306                 closable:false,
33307                 minWidth: this.minProgressWidth,
33308                 modal : true
33309             });
33310             return this;
33311         },
33312
33313         /**
33314          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33315          * If a callback function is passed it will be called after the user clicks the button, and the
33316          * id of the button that was clicked will be passed as the only parameter to the callback
33317          * (could also be the top-right close button).
33318          * @param {String} title The title bar text
33319          * @param {String} msg The message box body text
33320          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33321          * @param {Object} scope (optional) The scope of the callback function
33322          * @return {Roo.MessageBox} This message box
33323          */
33324         alert : function(title, msg, fn, scope){
33325             this.show({
33326                 title : title,
33327                 msg : msg,
33328                 buttons: this.OK,
33329                 fn: fn,
33330                 scope : scope,
33331                 modal : true
33332             });
33333             return this;
33334         },
33335
33336         /**
33337          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33338          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33339          * You are responsible for closing the message box when the process is complete.
33340          * @param {String} msg The message box body text
33341          * @param {String} title (optional) The title bar text
33342          * @return {Roo.MessageBox} This message box
33343          */
33344         wait : function(msg, title){
33345             this.show({
33346                 title : title,
33347                 msg : msg,
33348                 buttons: false,
33349                 closable:false,
33350                 progress:true,
33351                 modal:true,
33352                 width:300,
33353                 wait:true
33354             });
33355             waitTimer = Roo.TaskMgr.start({
33356                 run: function(i){
33357                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33358                 },
33359                 interval: 1000
33360             });
33361             return this;
33362         },
33363
33364         /**
33365          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33366          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33367          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33368          * @param {String} title The title bar text
33369          * @param {String} msg The message box body text
33370          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33371          * @param {Object} scope (optional) The scope of the callback function
33372          * @return {Roo.MessageBox} This message box
33373          */
33374         confirm : function(title, msg, fn, scope){
33375             this.show({
33376                 title : title,
33377                 msg : msg,
33378                 buttons: this.YESNO,
33379                 fn: fn,
33380                 scope : scope,
33381                 modal : true
33382             });
33383             return this;
33384         },
33385
33386         /**
33387          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33388          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33389          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33390          * (could also be the top-right close button) and the text that was entered will be passed as the two
33391          * parameters to the callback.
33392          * @param {String} title The title bar text
33393          * @param {String} msg The message box body text
33394          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33395          * @param {Object} scope (optional) The scope of the callback function
33396          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33397          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33398          * @return {Roo.MessageBox} This message box
33399          */
33400         prompt : function(title, msg, fn, scope, multiline){
33401             this.show({
33402                 title : title,
33403                 msg : msg,
33404                 buttons: this.OKCANCEL,
33405                 fn: fn,
33406                 minWidth:250,
33407                 scope : scope,
33408                 prompt:true,
33409                 multiline: multiline,
33410                 modal : true
33411             });
33412             return this;
33413         },
33414
33415         /**
33416          * Button config that displays a single OK button
33417          * @type Object
33418          */
33419         OK : {ok:true},
33420         /**
33421          * Button config that displays Yes and No buttons
33422          * @type Object
33423          */
33424         YESNO : {yes:true, no:true},
33425         /**
33426          * Button config that displays OK and Cancel buttons
33427          * @type Object
33428          */
33429         OKCANCEL : {ok:true, cancel:true},
33430         /**
33431          * Button config that displays Yes, No and Cancel buttons
33432          * @type Object
33433          */
33434         YESNOCANCEL : {yes:true, no:true, cancel:true},
33435
33436         /**
33437          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33438          * @type Number
33439          */
33440         defaultTextHeight : 75,
33441         /**
33442          * The maximum width in pixels of the message box (defaults to 600)
33443          * @type Number
33444          */
33445         maxWidth : 600,
33446         /**
33447          * The minimum width in pixels of the message box (defaults to 100)
33448          * @type Number
33449          */
33450         minWidth : 100,
33451         /**
33452          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33453          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33454          * @type Number
33455          */
33456         minProgressWidth : 250,
33457         /**
33458          * An object containing the default button text strings that can be overriden for localized language support.
33459          * Supported properties are: ok, cancel, yes and no.
33460          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33461          * @type Object
33462          */
33463         buttonText : {
33464             ok : "OK",
33465             cancel : "Cancel",
33466             yes : "Yes",
33467             no : "No"
33468         }
33469     };
33470 }();
33471
33472 /**
33473  * Shorthand for {@link Roo.MessageBox}
33474  */
33475 Roo.Msg = Roo.MessageBox;/*
33476  * Based on:
33477  * Ext JS Library 1.1.1
33478  * Copyright(c) 2006-2007, Ext JS, LLC.
33479  *
33480  * Originally Released Under LGPL - original licence link has changed is not relivant.
33481  *
33482  * Fork - LGPL
33483  * <script type="text/javascript">
33484  */
33485 /**
33486  * @class Roo.QuickTips
33487  * Provides attractive and customizable tooltips for any element.
33488  * @singleton
33489  */
33490 Roo.QuickTips = function(){
33491     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33492     var ce, bd, xy, dd;
33493     var visible = false, disabled = true, inited = false;
33494     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33495     
33496     var onOver = function(e){
33497         if(disabled){
33498             return;
33499         }
33500         var t = e.getTarget();
33501         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33502             return;
33503         }
33504         if(ce && t == ce.el){
33505             clearTimeout(hideProc);
33506             return;
33507         }
33508         if(t && tagEls[t.id]){
33509             tagEls[t.id].el = t;
33510             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33511             return;
33512         }
33513         var ttp, et = Roo.fly(t);
33514         var ns = cfg.namespace;
33515         if(tm.interceptTitles && t.title){
33516             ttp = t.title;
33517             t.qtip = ttp;
33518             t.removeAttribute("title");
33519             e.preventDefault();
33520         }else{
33521             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33522         }
33523         if(ttp){
33524             showProc = show.defer(tm.showDelay, tm, [{
33525                 el: t, 
33526                 text: ttp, 
33527                 width: et.getAttributeNS(ns, cfg.width),
33528                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33529                 title: et.getAttributeNS(ns, cfg.title),
33530                     cls: et.getAttributeNS(ns, cfg.cls)
33531             }]);
33532         }
33533     };
33534     
33535     var onOut = function(e){
33536         clearTimeout(showProc);
33537         var t = e.getTarget();
33538         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33539             hideProc = setTimeout(hide, tm.hideDelay);
33540         }
33541     };
33542     
33543     var onMove = function(e){
33544         if(disabled){
33545             return;
33546         }
33547         xy = e.getXY();
33548         xy[1] += 18;
33549         if(tm.trackMouse && ce){
33550             el.setXY(xy);
33551         }
33552     };
33553     
33554     var onDown = function(e){
33555         clearTimeout(showProc);
33556         clearTimeout(hideProc);
33557         if(!e.within(el)){
33558             if(tm.hideOnClick){
33559                 hide();
33560                 tm.disable();
33561                 tm.enable.defer(100, tm);
33562             }
33563         }
33564     };
33565     
33566     var getPad = function(){
33567         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33568     };
33569
33570     var show = function(o){
33571         if(disabled){
33572             return;
33573         }
33574         clearTimeout(dismissProc);
33575         ce = o;
33576         if(removeCls){ // in case manually hidden
33577             el.removeClass(removeCls);
33578             removeCls = null;
33579         }
33580         if(ce.cls){
33581             el.addClass(ce.cls);
33582             removeCls = ce.cls;
33583         }
33584         if(ce.title){
33585             tipTitle.update(ce.title);
33586             tipTitle.show();
33587         }else{
33588             tipTitle.update('');
33589             tipTitle.hide();
33590         }
33591         el.dom.style.width  = tm.maxWidth+'px';
33592         //tipBody.dom.style.width = '';
33593         tipBodyText.update(o.text);
33594         var p = getPad(), w = ce.width;
33595         if(!w){
33596             var td = tipBodyText.dom;
33597             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33598             if(aw > tm.maxWidth){
33599                 w = tm.maxWidth;
33600             }else if(aw < tm.minWidth){
33601                 w = tm.minWidth;
33602             }else{
33603                 w = aw;
33604             }
33605         }
33606         //tipBody.setWidth(w);
33607         el.setWidth(parseInt(w, 10) + p);
33608         if(ce.autoHide === false){
33609             close.setDisplayed(true);
33610             if(dd){
33611                 dd.unlock();
33612             }
33613         }else{
33614             close.setDisplayed(false);
33615             if(dd){
33616                 dd.lock();
33617             }
33618         }
33619         if(xy){
33620             el.avoidY = xy[1]-18;
33621             el.setXY(xy);
33622         }
33623         if(tm.animate){
33624             el.setOpacity(.1);
33625             el.setStyle("visibility", "visible");
33626             el.fadeIn({callback: afterShow});
33627         }else{
33628             afterShow();
33629         }
33630     };
33631     
33632     var afterShow = function(){
33633         if(ce){
33634             el.show();
33635             esc.enable();
33636             if(tm.autoDismiss && ce.autoHide !== false){
33637                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33638             }
33639         }
33640     };
33641     
33642     var hide = function(noanim){
33643         clearTimeout(dismissProc);
33644         clearTimeout(hideProc);
33645         ce = null;
33646         if(el.isVisible()){
33647             esc.disable();
33648             if(noanim !== true && tm.animate){
33649                 el.fadeOut({callback: afterHide});
33650             }else{
33651                 afterHide();
33652             } 
33653         }
33654     };
33655     
33656     var afterHide = function(){
33657         el.hide();
33658         if(removeCls){
33659             el.removeClass(removeCls);
33660             removeCls = null;
33661         }
33662     };
33663     
33664     return {
33665         /**
33666         * @cfg {Number} minWidth
33667         * The minimum width of the quick tip (defaults to 40)
33668         */
33669        minWidth : 40,
33670         /**
33671         * @cfg {Number} maxWidth
33672         * The maximum width of the quick tip (defaults to 300)
33673         */
33674        maxWidth : 300,
33675         /**
33676         * @cfg {Boolean} interceptTitles
33677         * True to automatically use the element's DOM title value if available (defaults to false)
33678         */
33679        interceptTitles : false,
33680         /**
33681         * @cfg {Boolean} trackMouse
33682         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33683         */
33684        trackMouse : false,
33685         /**
33686         * @cfg {Boolean} hideOnClick
33687         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33688         */
33689        hideOnClick : true,
33690         /**
33691         * @cfg {Number} showDelay
33692         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33693         */
33694        showDelay : 500,
33695         /**
33696         * @cfg {Number} hideDelay
33697         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33698         */
33699        hideDelay : 200,
33700         /**
33701         * @cfg {Boolean} autoHide
33702         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33703         * Used in conjunction with hideDelay.
33704         */
33705        autoHide : true,
33706         /**
33707         * @cfg {Boolean}
33708         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33709         * (defaults to true).  Used in conjunction with autoDismissDelay.
33710         */
33711        autoDismiss : true,
33712         /**
33713         * @cfg {Number}
33714         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33715         */
33716        autoDismissDelay : 5000,
33717        /**
33718         * @cfg {Boolean} animate
33719         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33720         */
33721        animate : false,
33722
33723        /**
33724         * @cfg {String} title
33725         * Title text to display (defaults to '').  This can be any valid HTML markup.
33726         */
33727         title: '',
33728        /**
33729         * @cfg {String} text
33730         * Body text to display (defaults to '').  This can be any valid HTML markup.
33731         */
33732         text : '',
33733        /**
33734         * @cfg {String} cls
33735         * A CSS class to apply to the base quick tip element (defaults to '').
33736         */
33737         cls : '',
33738        /**
33739         * @cfg {Number} width
33740         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33741         * minWidth or maxWidth.
33742         */
33743         width : null,
33744
33745     /**
33746      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33747      * or display QuickTips in a page.
33748      */
33749        init : function(){
33750           tm = Roo.QuickTips;
33751           cfg = tm.tagConfig;
33752           if(!inited){
33753               if(!Roo.isReady){ // allow calling of init() before onReady
33754                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33755                   return;
33756               }
33757               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33758               el.fxDefaults = {stopFx: true};
33759               // maximum custom styling
33760               //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>');
33761               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>');              
33762               tipTitle = el.child('h3');
33763               tipTitle.enableDisplayMode("block");
33764               tipBody = el.child('div.x-tip-bd');
33765               tipBodyText = el.child('div.x-tip-bd-inner');
33766               //bdLeft = el.child('div.x-tip-bd-left');
33767               //bdRight = el.child('div.x-tip-bd-right');
33768               close = el.child('div.x-tip-close');
33769               close.enableDisplayMode("block");
33770               close.on("click", hide);
33771               var d = Roo.get(document);
33772               d.on("mousedown", onDown);
33773               d.on("mouseover", onOver);
33774               d.on("mouseout", onOut);
33775               d.on("mousemove", onMove);
33776               esc = d.addKeyListener(27, hide);
33777               esc.disable();
33778               if(Roo.dd.DD){
33779                   dd = el.initDD("default", null, {
33780                       onDrag : function(){
33781                           el.sync();  
33782                       }
33783                   });
33784                   dd.setHandleElId(tipTitle.id);
33785                   dd.lock();
33786               }
33787               inited = true;
33788           }
33789           this.enable(); 
33790        },
33791
33792     /**
33793      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33794      * are supported:
33795      * <pre>
33796 Property    Type                   Description
33797 ----------  ---------------------  ------------------------------------------------------------------------
33798 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33799      * </ul>
33800      * @param {Object} config The config object
33801      */
33802        register : function(config){
33803            var cs = config instanceof Array ? config : arguments;
33804            for(var i = 0, len = cs.length; i < len; i++) {
33805                var c = cs[i];
33806                var target = c.target;
33807                if(target){
33808                    if(target instanceof Array){
33809                        for(var j = 0, jlen = target.length; j < jlen; j++){
33810                            tagEls[target[j]] = c;
33811                        }
33812                    }else{
33813                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33814                    }
33815                }
33816            }
33817        },
33818
33819     /**
33820      * Removes this quick tip from its element and destroys it.
33821      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33822      */
33823        unregister : function(el){
33824            delete tagEls[Roo.id(el)];
33825        },
33826
33827     /**
33828      * Enable this quick tip.
33829      */
33830        enable : function(){
33831            if(inited && disabled){
33832                locks.pop();
33833                if(locks.length < 1){
33834                    disabled = false;
33835                }
33836            }
33837        },
33838
33839     /**
33840      * Disable this quick tip.
33841      */
33842        disable : function(){
33843           disabled = true;
33844           clearTimeout(showProc);
33845           clearTimeout(hideProc);
33846           clearTimeout(dismissProc);
33847           if(ce){
33848               hide(true);
33849           }
33850           locks.push(1);
33851        },
33852
33853     /**
33854      * Returns true if the quick tip is enabled, else false.
33855      */
33856        isEnabled : function(){
33857             return !disabled;
33858        },
33859
33860         // private
33861        tagConfig : {
33862            namespace : "roo", // was ext?? this may break..
33863            alt_namespace : "ext",
33864            attribute : "qtip",
33865            width : "width",
33866            target : "target",
33867            title : "qtitle",
33868            hide : "hide",
33869            cls : "qclass"
33870        }
33871    };
33872 }();
33873
33874 // backwards compat
33875 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33876  * Based on:
33877  * Ext JS Library 1.1.1
33878  * Copyright(c) 2006-2007, Ext JS, LLC.
33879  *
33880  * Originally Released Under LGPL - original licence link has changed is not relivant.
33881  *
33882  * Fork - LGPL
33883  * <script type="text/javascript">
33884  */
33885  
33886
33887 /**
33888  * @class Roo.tree.TreePanel
33889  * @extends Roo.data.Tree
33890
33891  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33892  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33893  * @cfg {Boolean} enableDD true to enable drag and drop
33894  * @cfg {Boolean} enableDrag true to enable just drag
33895  * @cfg {Boolean} enableDrop true to enable just drop
33896  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33897  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33898  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33899  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33900  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33901  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33902  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33903  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33904  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33905  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33906  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33907  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33908  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33909  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33910  * @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>
33911  * @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>
33912  * 
33913  * @constructor
33914  * @param {String/HTMLElement/Element} el The container element
33915  * @param {Object} config
33916  */
33917 Roo.tree.TreePanel = function(el, config){
33918     var root = false;
33919     var loader = false;
33920     if (config.root) {
33921         root = config.root;
33922         delete config.root;
33923     }
33924     if (config.loader) {
33925         loader = config.loader;
33926         delete config.loader;
33927     }
33928     
33929     Roo.apply(this, config);
33930     Roo.tree.TreePanel.superclass.constructor.call(this);
33931     this.el = Roo.get(el);
33932     this.el.addClass('x-tree');
33933     //console.log(root);
33934     if (root) {
33935         this.setRootNode( Roo.factory(root, Roo.tree));
33936     }
33937     if (loader) {
33938         this.loader = Roo.factory(loader, Roo.tree);
33939     }
33940    /**
33941     * Read-only. The id of the container element becomes this TreePanel's id.
33942     */
33943     this.id = this.el.id;
33944     this.addEvents({
33945         /**
33946         * @event beforeload
33947         * Fires before a node is loaded, return false to cancel
33948         * @param {Node} node The node being loaded
33949         */
33950         "beforeload" : true,
33951         /**
33952         * @event load
33953         * Fires when a node is loaded
33954         * @param {Node} node The node that was loaded
33955         */
33956         "load" : true,
33957         /**
33958         * @event textchange
33959         * Fires when the text for a node is changed
33960         * @param {Node} node The node
33961         * @param {String} text The new text
33962         * @param {String} oldText The old text
33963         */
33964         "textchange" : true,
33965         /**
33966         * @event beforeexpand
33967         * Fires before a node is expanded, return false to cancel.
33968         * @param {Node} node The node
33969         * @param {Boolean} deep
33970         * @param {Boolean} anim
33971         */
33972         "beforeexpand" : true,
33973         /**
33974         * @event beforecollapse
33975         * Fires before a node is collapsed, return false to cancel.
33976         * @param {Node} node The node
33977         * @param {Boolean} deep
33978         * @param {Boolean} anim
33979         */
33980         "beforecollapse" : true,
33981         /**
33982         * @event expand
33983         * Fires when a node is expanded
33984         * @param {Node} node The node
33985         */
33986         "expand" : true,
33987         /**
33988         * @event disabledchange
33989         * Fires when the disabled status of a node changes
33990         * @param {Node} node The node
33991         * @param {Boolean} disabled
33992         */
33993         "disabledchange" : true,
33994         /**
33995         * @event collapse
33996         * Fires when a node is collapsed
33997         * @param {Node} node The node
33998         */
33999         "collapse" : true,
34000         /**
34001         * @event beforeclick
34002         * Fires before click processing on a node. Return false to cancel the default action.
34003         * @param {Node} node The node
34004         * @param {Roo.EventObject} e The event object
34005         */
34006         "beforeclick":true,
34007         /**
34008         * @event checkchange
34009         * Fires when a node with a checkbox's checked property changes
34010         * @param {Node} this This node
34011         * @param {Boolean} checked
34012         */
34013         "checkchange":true,
34014         /**
34015         * @event click
34016         * Fires when a node is clicked
34017         * @param {Node} node The node
34018         * @param {Roo.EventObject} e The event object
34019         */
34020         "click":true,
34021         /**
34022         * @event dblclick
34023         * Fires when a node is double clicked
34024         * @param {Node} node The node
34025         * @param {Roo.EventObject} e The event object
34026         */
34027         "dblclick":true,
34028         /**
34029         * @event contextmenu
34030         * Fires when a node is right clicked
34031         * @param {Node} node The node
34032         * @param {Roo.EventObject} e The event object
34033         */
34034         "contextmenu":true,
34035         /**
34036         * @event beforechildrenrendered
34037         * Fires right before the child nodes for a node are rendered
34038         * @param {Node} node The node
34039         */
34040         "beforechildrenrendered":true,
34041         /**
34042         * @event startdrag
34043         * Fires when a node starts being dragged
34044         * @param {Roo.tree.TreePanel} this
34045         * @param {Roo.tree.TreeNode} node
34046         * @param {event} e The raw browser event
34047         */ 
34048        "startdrag" : true,
34049        /**
34050         * @event enddrag
34051         * Fires when a drag operation is complete
34052         * @param {Roo.tree.TreePanel} this
34053         * @param {Roo.tree.TreeNode} node
34054         * @param {event} e The raw browser event
34055         */
34056        "enddrag" : true,
34057        /**
34058         * @event dragdrop
34059         * Fires when a dragged node is dropped on a valid DD target
34060         * @param {Roo.tree.TreePanel} this
34061         * @param {Roo.tree.TreeNode} node
34062         * @param {DD} dd The dd it was dropped on
34063         * @param {event} e The raw browser event
34064         */
34065        "dragdrop" : true,
34066        /**
34067         * @event beforenodedrop
34068         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34069         * passed to handlers has the following properties:<br />
34070         * <ul style="padding:5px;padding-left:16px;">
34071         * <li>tree - The TreePanel</li>
34072         * <li>target - The node being targeted for the drop</li>
34073         * <li>data - The drag data from the drag source</li>
34074         * <li>point - The point of the drop - append, above or below</li>
34075         * <li>source - The drag source</li>
34076         * <li>rawEvent - Raw mouse event</li>
34077         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34078         * to be inserted by setting them on this object.</li>
34079         * <li>cancel - Set this to true to cancel the drop.</li>
34080         * </ul>
34081         * @param {Object} dropEvent
34082         */
34083        "beforenodedrop" : true,
34084        /**
34085         * @event nodedrop
34086         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34087         * passed to handlers has the following properties:<br />
34088         * <ul style="padding:5px;padding-left:16px;">
34089         * <li>tree - The TreePanel</li>
34090         * <li>target - The node being targeted for the drop</li>
34091         * <li>data - The drag data from the drag source</li>
34092         * <li>point - The point of the drop - append, above or below</li>
34093         * <li>source - The drag source</li>
34094         * <li>rawEvent - Raw mouse event</li>
34095         * <li>dropNode - Dropped node(s).</li>
34096         * </ul>
34097         * @param {Object} dropEvent
34098         */
34099        "nodedrop" : true,
34100         /**
34101         * @event nodedragover
34102         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34103         * passed to handlers has the following properties:<br />
34104         * <ul style="padding:5px;padding-left:16px;">
34105         * <li>tree - The TreePanel</li>
34106         * <li>target - The node being targeted for the drop</li>
34107         * <li>data - The drag data from the drag source</li>
34108         * <li>point - The point of the drop - append, above or below</li>
34109         * <li>source - The drag source</li>
34110         * <li>rawEvent - Raw mouse event</li>
34111         * <li>dropNode - Drop node(s) provided by the source.</li>
34112         * <li>cancel - Set this to true to signal drop not allowed.</li>
34113         * </ul>
34114         * @param {Object} dragOverEvent
34115         */
34116        "nodedragover" : true
34117         
34118     });
34119     if(this.singleExpand){
34120        this.on("beforeexpand", this.restrictExpand, this);
34121     }
34122     if (this.editor) {
34123         this.editor.tree = this;
34124         this.editor = Roo.factory(this.editor, Roo.tree);
34125     }
34126     
34127     if (this.selModel) {
34128         this.selModel = Roo.factory(this.selModel, Roo.tree);
34129     }
34130    
34131 };
34132 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34133     rootVisible : true,
34134     animate: Roo.enableFx,
34135     lines : true,
34136     enableDD : false,
34137     hlDrop : Roo.enableFx,
34138   
34139     renderer: false,
34140     
34141     rendererTip: false,
34142     // private
34143     restrictExpand : function(node){
34144         var p = node.parentNode;
34145         if(p){
34146             if(p.expandedChild && p.expandedChild.parentNode == p){
34147                 p.expandedChild.collapse();
34148             }
34149             p.expandedChild = node;
34150         }
34151     },
34152
34153     // private override
34154     setRootNode : function(node){
34155         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34156         if(!this.rootVisible){
34157             node.ui = new Roo.tree.RootTreeNodeUI(node);
34158         }
34159         return node;
34160     },
34161
34162     /**
34163      * Returns the container element for this TreePanel
34164      */
34165     getEl : function(){
34166         return this.el;
34167     },
34168
34169     /**
34170      * Returns the default TreeLoader for this TreePanel
34171      */
34172     getLoader : function(){
34173         return this.loader;
34174     },
34175
34176     /**
34177      * Expand all nodes
34178      */
34179     expandAll : function(){
34180         this.root.expand(true);
34181     },
34182
34183     /**
34184      * Collapse all nodes
34185      */
34186     collapseAll : function(){
34187         this.root.collapse(true);
34188     },
34189
34190     /**
34191      * Returns the selection model used by this TreePanel
34192      */
34193     getSelectionModel : function(){
34194         if(!this.selModel){
34195             this.selModel = new Roo.tree.DefaultSelectionModel();
34196         }
34197         return this.selModel;
34198     },
34199
34200     /**
34201      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34202      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34203      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34204      * @return {Array}
34205      */
34206     getChecked : function(a, startNode){
34207         startNode = startNode || this.root;
34208         var r = [];
34209         var f = function(){
34210             if(this.attributes.checked){
34211                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34212             }
34213         }
34214         startNode.cascade(f);
34215         return r;
34216     },
34217
34218     /**
34219      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34220      * @param {String} path
34221      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34222      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34223      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34224      */
34225     expandPath : function(path, attr, callback){
34226         attr = attr || "id";
34227         var keys = path.split(this.pathSeparator);
34228         var curNode = this.root;
34229         if(curNode.attributes[attr] != keys[1]){ // invalid root
34230             if(callback){
34231                 callback(false, null);
34232             }
34233             return;
34234         }
34235         var index = 1;
34236         var f = function(){
34237             if(++index == keys.length){
34238                 if(callback){
34239                     callback(true, curNode);
34240                 }
34241                 return;
34242             }
34243             var c = curNode.findChild(attr, keys[index]);
34244             if(!c){
34245                 if(callback){
34246                     callback(false, curNode);
34247                 }
34248                 return;
34249             }
34250             curNode = c;
34251             c.expand(false, false, f);
34252         };
34253         curNode.expand(false, false, f);
34254     },
34255
34256     /**
34257      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34258      * @param {String} path
34259      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34260      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34261      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34262      */
34263     selectPath : function(path, attr, callback){
34264         attr = attr || "id";
34265         var keys = path.split(this.pathSeparator);
34266         var v = keys.pop();
34267         if(keys.length > 0){
34268             var f = function(success, node){
34269                 if(success && node){
34270                     var n = node.findChild(attr, v);
34271                     if(n){
34272                         n.select();
34273                         if(callback){
34274                             callback(true, n);
34275                         }
34276                     }else if(callback){
34277                         callback(false, n);
34278                     }
34279                 }else{
34280                     if(callback){
34281                         callback(false, n);
34282                     }
34283                 }
34284             };
34285             this.expandPath(keys.join(this.pathSeparator), attr, f);
34286         }else{
34287             this.root.select();
34288             if(callback){
34289                 callback(true, this.root);
34290             }
34291         }
34292     },
34293
34294     getTreeEl : function(){
34295         return this.el;
34296     },
34297
34298     /**
34299      * Trigger rendering of this TreePanel
34300      */
34301     render : function(){
34302         if (this.innerCt) {
34303             return this; // stop it rendering more than once!!
34304         }
34305         
34306         this.innerCt = this.el.createChild({tag:"ul",
34307                cls:"x-tree-root-ct " +
34308                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34309
34310         if(this.containerScroll){
34311             Roo.dd.ScrollManager.register(this.el);
34312         }
34313         if((this.enableDD || this.enableDrop) && !this.dropZone){
34314            /**
34315             * The dropZone used by this tree if drop is enabled
34316             * @type Roo.tree.TreeDropZone
34317             */
34318              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34319                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34320            });
34321         }
34322         if((this.enableDD || this.enableDrag) && !this.dragZone){
34323            /**
34324             * The dragZone used by this tree if drag is enabled
34325             * @type Roo.tree.TreeDragZone
34326             */
34327             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34328                ddGroup: this.ddGroup || "TreeDD",
34329                scroll: this.ddScroll
34330            });
34331         }
34332         this.getSelectionModel().init(this);
34333         if (!this.root) {
34334             Roo.log("ROOT not set in tree");
34335             return this;
34336         }
34337         this.root.render();
34338         if(!this.rootVisible){
34339             this.root.renderChildren();
34340         }
34341         return this;
34342     }
34343 });/*
34344  * Based on:
34345  * Ext JS Library 1.1.1
34346  * Copyright(c) 2006-2007, Ext JS, LLC.
34347  *
34348  * Originally Released Under LGPL - original licence link has changed is not relivant.
34349  *
34350  * Fork - LGPL
34351  * <script type="text/javascript">
34352  */
34353  
34354
34355 /**
34356  * @class Roo.tree.DefaultSelectionModel
34357  * @extends Roo.util.Observable
34358  * The default single selection for a TreePanel.
34359  * @param {Object} cfg Configuration
34360  */
34361 Roo.tree.DefaultSelectionModel = function(cfg){
34362    this.selNode = null;
34363    
34364    
34365    
34366    this.addEvents({
34367        /**
34368         * @event selectionchange
34369         * Fires when the selected node changes
34370         * @param {DefaultSelectionModel} this
34371         * @param {TreeNode} node the new selection
34372         */
34373        "selectionchange" : true,
34374
34375        /**
34376         * @event beforeselect
34377         * Fires before the selected node changes, return false to cancel the change
34378         * @param {DefaultSelectionModel} this
34379         * @param {TreeNode} node the new selection
34380         * @param {TreeNode} node the old selection
34381         */
34382        "beforeselect" : true
34383    });
34384    
34385     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34386 };
34387
34388 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34389     init : function(tree){
34390         this.tree = tree;
34391         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34392         tree.on("click", this.onNodeClick, this);
34393     },
34394     
34395     onNodeClick : function(node, e){
34396         if (e.ctrlKey && this.selNode == node)  {
34397             this.unselect(node);
34398             return;
34399         }
34400         this.select(node);
34401     },
34402     
34403     /**
34404      * Select a node.
34405      * @param {TreeNode} node The node to select
34406      * @return {TreeNode} The selected node
34407      */
34408     select : function(node){
34409         var last = this.selNode;
34410         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34411             if(last){
34412                 last.ui.onSelectedChange(false);
34413             }
34414             this.selNode = node;
34415             node.ui.onSelectedChange(true);
34416             this.fireEvent("selectionchange", this, node, last);
34417         }
34418         return node;
34419     },
34420     
34421     /**
34422      * Deselect a node.
34423      * @param {TreeNode} node The node to unselect
34424      */
34425     unselect : function(node){
34426         if(this.selNode == node){
34427             this.clearSelections();
34428         }    
34429     },
34430     
34431     /**
34432      * Clear all selections
34433      */
34434     clearSelections : function(){
34435         var n = this.selNode;
34436         if(n){
34437             n.ui.onSelectedChange(false);
34438             this.selNode = null;
34439             this.fireEvent("selectionchange", this, null);
34440         }
34441         return n;
34442     },
34443     
34444     /**
34445      * Get the selected node
34446      * @return {TreeNode} The selected node
34447      */
34448     getSelectedNode : function(){
34449         return this.selNode;    
34450     },
34451     
34452     /**
34453      * Returns true if the node is selected
34454      * @param {TreeNode} node The node to check
34455      * @return {Boolean}
34456      */
34457     isSelected : function(node){
34458         return this.selNode == node;  
34459     },
34460
34461     /**
34462      * Selects the node above the selected node in the tree, intelligently walking the nodes
34463      * @return TreeNode The new selection
34464      */
34465     selectPrevious : function(){
34466         var s = this.selNode || this.lastSelNode;
34467         if(!s){
34468             return null;
34469         }
34470         var ps = s.previousSibling;
34471         if(ps){
34472             if(!ps.isExpanded() || ps.childNodes.length < 1){
34473                 return this.select(ps);
34474             } else{
34475                 var lc = ps.lastChild;
34476                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34477                     lc = lc.lastChild;
34478                 }
34479                 return this.select(lc);
34480             }
34481         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34482             return this.select(s.parentNode);
34483         }
34484         return null;
34485     },
34486
34487     /**
34488      * Selects the node above the selected node in the tree, intelligently walking the nodes
34489      * @return TreeNode The new selection
34490      */
34491     selectNext : function(){
34492         var s = this.selNode || this.lastSelNode;
34493         if(!s){
34494             return null;
34495         }
34496         if(s.firstChild && s.isExpanded()){
34497              return this.select(s.firstChild);
34498          }else if(s.nextSibling){
34499              return this.select(s.nextSibling);
34500          }else if(s.parentNode){
34501             var newS = null;
34502             s.parentNode.bubble(function(){
34503                 if(this.nextSibling){
34504                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34505                     return false;
34506                 }
34507             });
34508             return newS;
34509          }
34510         return null;
34511     },
34512
34513     onKeyDown : function(e){
34514         var s = this.selNode || this.lastSelNode;
34515         // undesirable, but required
34516         var sm = this;
34517         if(!s){
34518             return;
34519         }
34520         var k = e.getKey();
34521         switch(k){
34522              case e.DOWN:
34523                  e.stopEvent();
34524                  this.selectNext();
34525              break;
34526              case e.UP:
34527                  e.stopEvent();
34528                  this.selectPrevious();
34529              break;
34530              case e.RIGHT:
34531                  e.preventDefault();
34532                  if(s.hasChildNodes()){
34533                      if(!s.isExpanded()){
34534                          s.expand();
34535                      }else if(s.firstChild){
34536                          this.select(s.firstChild, e);
34537                      }
34538                  }
34539              break;
34540              case e.LEFT:
34541                  e.preventDefault();
34542                  if(s.hasChildNodes() && s.isExpanded()){
34543                      s.collapse();
34544                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34545                      this.select(s.parentNode, e);
34546                  }
34547              break;
34548         };
34549     }
34550 });
34551
34552 /**
34553  * @class Roo.tree.MultiSelectionModel
34554  * @extends Roo.util.Observable
34555  * Multi selection for a TreePanel.
34556  * @param {Object} cfg Configuration
34557  */
34558 Roo.tree.MultiSelectionModel = function(){
34559    this.selNodes = [];
34560    this.selMap = {};
34561    this.addEvents({
34562        /**
34563         * @event selectionchange
34564         * Fires when the selected nodes change
34565         * @param {MultiSelectionModel} this
34566         * @param {Array} nodes Array of the selected nodes
34567         */
34568        "selectionchange" : true
34569    });
34570    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34571    
34572 };
34573
34574 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34575     init : function(tree){
34576         this.tree = tree;
34577         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34578         tree.on("click", this.onNodeClick, this);
34579     },
34580     
34581     onNodeClick : function(node, e){
34582         this.select(node, e, e.ctrlKey);
34583     },
34584     
34585     /**
34586      * Select a node.
34587      * @param {TreeNode} node The node to select
34588      * @param {EventObject} e (optional) An event associated with the selection
34589      * @param {Boolean} keepExisting True to retain existing selections
34590      * @return {TreeNode} The selected node
34591      */
34592     select : function(node, e, keepExisting){
34593         if(keepExisting !== true){
34594             this.clearSelections(true);
34595         }
34596         if(this.isSelected(node)){
34597             this.lastSelNode = node;
34598             return node;
34599         }
34600         this.selNodes.push(node);
34601         this.selMap[node.id] = node;
34602         this.lastSelNode = node;
34603         node.ui.onSelectedChange(true);
34604         this.fireEvent("selectionchange", this, this.selNodes);
34605         return node;
34606     },
34607     
34608     /**
34609      * Deselect a node.
34610      * @param {TreeNode} node The node to unselect
34611      */
34612     unselect : function(node){
34613         if(this.selMap[node.id]){
34614             node.ui.onSelectedChange(false);
34615             var sn = this.selNodes;
34616             var index = -1;
34617             if(sn.indexOf){
34618                 index = sn.indexOf(node);
34619             }else{
34620                 for(var i = 0, len = sn.length; i < len; i++){
34621                     if(sn[i] == node){
34622                         index = i;
34623                         break;
34624                     }
34625                 }
34626             }
34627             if(index != -1){
34628                 this.selNodes.splice(index, 1);
34629             }
34630             delete this.selMap[node.id];
34631             this.fireEvent("selectionchange", this, this.selNodes);
34632         }
34633     },
34634     
34635     /**
34636      * Clear all selections
34637      */
34638     clearSelections : function(suppressEvent){
34639         var sn = this.selNodes;
34640         if(sn.length > 0){
34641             for(var i = 0, len = sn.length; i < len; i++){
34642                 sn[i].ui.onSelectedChange(false);
34643             }
34644             this.selNodes = [];
34645             this.selMap = {};
34646             if(suppressEvent !== true){
34647                 this.fireEvent("selectionchange", this, this.selNodes);
34648             }
34649         }
34650     },
34651     
34652     /**
34653      * Returns true if the node is selected
34654      * @param {TreeNode} node The node to check
34655      * @return {Boolean}
34656      */
34657     isSelected : function(node){
34658         return this.selMap[node.id] ? true : false;  
34659     },
34660     
34661     /**
34662      * Returns an array of the selected nodes
34663      * @return {Array}
34664      */
34665     getSelectedNodes : function(){
34666         return this.selNodes;    
34667     },
34668
34669     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34670
34671     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34672
34673     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34674 });/*
34675  * Based on:
34676  * Ext JS Library 1.1.1
34677  * Copyright(c) 2006-2007, Ext JS, LLC.
34678  *
34679  * Originally Released Under LGPL - original licence link has changed is not relivant.
34680  *
34681  * Fork - LGPL
34682  * <script type="text/javascript">
34683  */
34684  
34685 /**
34686  * @class Roo.tree.TreeNode
34687  * @extends Roo.data.Node
34688  * @cfg {String} text The text for this node
34689  * @cfg {Boolean} expanded true to start the node expanded
34690  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34691  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34692  * @cfg {Boolean} disabled true to start the node disabled
34693  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34694  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34695  * @cfg {String} cls A css class to be added to the node
34696  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34697  * @cfg {String} href URL of the link used for the node (defaults to #)
34698  * @cfg {String} hrefTarget target frame for the link
34699  * @cfg {String} qtip An Ext QuickTip for the node
34700  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34701  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34702  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34703  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34704  * (defaults to undefined with no checkbox rendered)
34705  * @constructor
34706  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34707  */
34708 Roo.tree.TreeNode = function(attributes){
34709     attributes = attributes || {};
34710     if(typeof attributes == "string"){
34711         attributes = {text: attributes};
34712     }
34713     this.childrenRendered = false;
34714     this.rendered = false;
34715     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34716     this.expanded = attributes.expanded === true;
34717     this.isTarget = attributes.isTarget !== false;
34718     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34719     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34720
34721     /**
34722      * Read-only. The text for this node. To change it use setText().
34723      * @type String
34724      */
34725     this.text = attributes.text;
34726     /**
34727      * True if this node is disabled.
34728      * @type Boolean
34729      */
34730     this.disabled = attributes.disabled === true;
34731
34732     this.addEvents({
34733         /**
34734         * @event textchange
34735         * Fires when the text for this node is changed
34736         * @param {Node} this This node
34737         * @param {String} text The new text
34738         * @param {String} oldText The old text
34739         */
34740         "textchange" : true,
34741         /**
34742         * @event beforeexpand
34743         * Fires before this node is expanded, return false to cancel.
34744         * @param {Node} this This node
34745         * @param {Boolean} deep
34746         * @param {Boolean} anim
34747         */
34748         "beforeexpand" : true,
34749         /**
34750         * @event beforecollapse
34751         * Fires before this node is collapsed, return false to cancel.
34752         * @param {Node} this This node
34753         * @param {Boolean} deep
34754         * @param {Boolean} anim
34755         */
34756         "beforecollapse" : true,
34757         /**
34758         * @event expand
34759         * Fires when this node is expanded
34760         * @param {Node} this This node
34761         */
34762         "expand" : true,
34763         /**
34764         * @event disabledchange
34765         * Fires when the disabled status of this node changes
34766         * @param {Node} this This node
34767         * @param {Boolean} disabled
34768         */
34769         "disabledchange" : true,
34770         /**
34771         * @event collapse
34772         * Fires when this node is collapsed
34773         * @param {Node} this This node
34774         */
34775         "collapse" : true,
34776         /**
34777         * @event beforeclick
34778         * Fires before click processing. Return false to cancel the default action.
34779         * @param {Node} this This node
34780         * @param {Roo.EventObject} e The event object
34781         */
34782         "beforeclick":true,
34783         /**
34784         * @event checkchange
34785         * Fires when a node with a checkbox's checked property changes
34786         * @param {Node} this This node
34787         * @param {Boolean} checked
34788         */
34789         "checkchange":true,
34790         /**
34791         * @event click
34792         * Fires when this node is clicked
34793         * @param {Node} this This node
34794         * @param {Roo.EventObject} e The event object
34795         */
34796         "click":true,
34797         /**
34798         * @event dblclick
34799         * Fires when this node is double clicked
34800         * @param {Node} this This node
34801         * @param {Roo.EventObject} e The event object
34802         */
34803         "dblclick":true,
34804         /**
34805         * @event contextmenu
34806         * Fires when this node is right clicked
34807         * @param {Node} this This node
34808         * @param {Roo.EventObject} e The event object
34809         */
34810         "contextmenu":true,
34811         /**
34812         * @event beforechildrenrendered
34813         * Fires right before the child nodes for this node are rendered
34814         * @param {Node} this This node
34815         */
34816         "beforechildrenrendered":true
34817     });
34818
34819     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34820
34821     /**
34822      * Read-only. The UI for this node
34823      * @type TreeNodeUI
34824      */
34825     this.ui = new uiClass(this);
34826     
34827     // finally support items[]
34828     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34829         return;
34830     }
34831     
34832     
34833     Roo.each(this.attributes.items, function(c) {
34834         this.appendChild(Roo.factory(c,Roo.Tree));
34835     }, this);
34836     delete this.attributes.items;
34837     
34838     
34839     
34840 };
34841 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34842     preventHScroll: true,
34843     /**
34844      * Returns true if this node is expanded
34845      * @return {Boolean}
34846      */
34847     isExpanded : function(){
34848         return this.expanded;
34849     },
34850
34851     /**
34852      * Returns the UI object for this node
34853      * @return {TreeNodeUI}
34854      */
34855     getUI : function(){
34856         return this.ui;
34857     },
34858
34859     // private override
34860     setFirstChild : function(node){
34861         var of = this.firstChild;
34862         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34863         if(this.childrenRendered && of && node != of){
34864             of.renderIndent(true, true);
34865         }
34866         if(this.rendered){
34867             this.renderIndent(true, true);
34868         }
34869     },
34870
34871     // private override
34872     setLastChild : function(node){
34873         var ol = this.lastChild;
34874         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34875         if(this.childrenRendered && ol && node != ol){
34876             ol.renderIndent(true, true);
34877         }
34878         if(this.rendered){
34879             this.renderIndent(true, true);
34880         }
34881     },
34882
34883     // these methods are overridden to provide lazy rendering support
34884     // private override
34885     appendChild : function()
34886     {
34887         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34888         if(node && this.childrenRendered){
34889             node.render();
34890         }
34891         this.ui.updateExpandIcon();
34892         return node;
34893     },
34894
34895     // private override
34896     removeChild : function(node){
34897         this.ownerTree.getSelectionModel().unselect(node);
34898         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34899         // if it's been rendered remove dom node
34900         if(this.childrenRendered){
34901             node.ui.remove();
34902         }
34903         if(this.childNodes.length < 1){
34904             this.collapse(false, false);
34905         }else{
34906             this.ui.updateExpandIcon();
34907         }
34908         if(!this.firstChild) {
34909             this.childrenRendered = false;
34910         }
34911         return node;
34912     },
34913
34914     // private override
34915     insertBefore : function(node, refNode){
34916         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34917         if(newNode && refNode && this.childrenRendered){
34918             node.render();
34919         }
34920         this.ui.updateExpandIcon();
34921         return newNode;
34922     },
34923
34924     /**
34925      * Sets the text for this node
34926      * @param {String} text
34927      */
34928     setText : function(text){
34929         var oldText = this.text;
34930         this.text = text;
34931         this.attributes.text = text;
34932         if(this.rendered){ // event without subscribing
34933             this.ui.onTextChange(this, text, oldText);
34934         }
34935         this.fireEvent("textchange", this, text, oldText);
34936     },
34937
34938     /**
34939      * Triggers selection of this node
34940      */
34941     select : function(){
34942         this.getOwnerTree().getSelectionModel().select(this);
34943     },
34944
34945     /**
34946      * Triggers deselection of this node
34947      */
34948     unselect : function(){
34949         this.getOwnerTree().getSelectionModel().unselect(this);
34950     },
34951
34952     /**
34953      * Returns true if this node is selected
34954      * @return {Boolean}
34955      */
34956     isSelected : function(){
34957         return this.getOwnerTree().getSelectionModel().isSelected(this);
34958     },
34959
34960     /**
34961      * Expand this node.
34962      * @param {Boolean} deep (optional) True to expand all children as well
34963      * @param {Boolean} anim (optional) false to cancel the default animation
34964      * @param {Function} callback (optional) A callback to be called when
34965      * expanding this node completes (does not wait for deep expand to complete).
34966      * Called with 1 parameter, this node.
34967      */
34968     expand : function(deep, anim, callback){
34969         if(!this.expanded){
34970             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34971                 return;
34972             }
34973             if(!this.childrenRendered){
34974                 this.renderChildren();
34975             }
34976             this.expanded = true;
34977             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34978                 this.ui.animExpand(function(){
34979                     this.fireEvent("expand", this);
34980                     if(typeof callback == "function"){
34981                         callback(this);
34982                     }
34983                     if(deep === true){
34984                         this.expandChildNodes(true);
34985                     }
34986                 }.createDelegate(this));
34987                 return;
34988             }else{
34989                 this.ui.expand();
34990                 this.fireEvent("expand", this);
34991                 if(typeof callback == "function"){
34992                     callback(this);
34993                 }
34994             }
34995         }else{
34996            if(typeof callback == "function"){
34997                callback(this);
34998            }
34999         }
35000         if(deep === true){
35001             this.expandChildNodes(true);
35002         }
35003     },
35004
35005     isHiddenRoot : function(){
35006         return this.isRoot && !this.getOwnerTree().rootVisible;
35007     },
35008
35009     /**
35010      * Collapse this node.
35011      * @param {Boolean} deep (optional) True to collapse all children as well
35012      * @param {Boolean} anim (optional) false to cancel the default animation
35013      */
35014     collapse : function(deep, anim){
35015         if(this.expanded && !this.isHiddenRoot()){
35016             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35017                 return;
35018             }
35019             this.expanded = false;
35020             if((this.getOwnerTree().animate && anim !== false) || anim){
35021                 this.ui.animCollapse(function(){
35022                     this.fireEvent("collapse", this);
35023                     if(deep === true){
35024                         this.collapseChildNodes(true);
35025                     }
35026                 }.createDelegate(this));
35027                 return;
35028             }else{
35029                 this.ui.collapse();
35030                 this.fireEvent("collapse", this);
35031             }
35032         }
35033         if(deep === true){
35034             var cs = this.childNodes;
35035             for(var i = 0, len = cs.length; i < len; i++) {
35036                 cs[i].collapse(true, false);
35037             }
35038         }
35039     },
35040
35041     // private
35042     delayedExpand : function(delay){
35043         if(!this.expandProcId){
35044             this.expandProcId = this.expand.defer(delay, this);
35045         }
35046     },
35047
35048     // private
35049     cancelExpand : function(){
35050         if(this.expandProcId){
35051             clearTimeout(this.expandProcId);
35052         }
35053         this.expandProcId = false;
35054     },
35055
35056     /**
35057      * Toggles expanded/collapsed state of the node
35058      */
35059     toggle : function(){
35060         if(this.expanded){
35061             this.collapse();
35062         }else{
35063             this.expand();
35064         }
35065     },
35066
35067     /**
35068      * Ensures all parent nodes are expanded
35069      */
35070     ensureVisible : function(callback){
35071         var tree = this.getOwnerTree();
35072         tree.expandPath(this.parentNode.getPath(), false, function(){
35073             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35074             Roo.callback(callback);
35075         }.createDelegate(this));
35076     },
35077
35078     /**
35079      * Expand all child nodes
35080      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35081      */
35082     expandChildNodes : function(deep){
35083         var cs = this.childNodes;
35084         for(var i = 0, len = cs.length; i < len; i++) {
35085                 cs[i].expand(deep);
35086         }
35087     },
35088
35089     /**
35090      * Collapse all child nodes
35091      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35092      */
35093     collapseChildNodes : function(deep){
35094         var cs = this.childNodes;
35095         for(var i = 0, len = cs.length; i < len; i++) {
35096                 cs[i].collapse(deep);
35097         }
35098     },
35099
35100     /**
35101      * Disables this node
35102      */
35103     disable : function(){
35104         this.disabled = true;
35105         this.unselect();
35106         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35107             this.ui.onDisableChange(this, true);
35108         }
35109         this.fireEvent("disabledchange", this, true);
35110     },
35111
35112     /**
35113      * Enables this node
35114      */
35115     enable : function(){
35116         this.disabled = false;
35117         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35118             this.ui.onDisableChange(this, false);
35119         }
35120         this.fireEvent("disabledchange", this, false);
35121     },
35122
35123     // private
35124     renderChildren : function(suppressEvent){
35125         if(suppressEvent !== false){
35126             this.fireEvent("beforechildrenrendered", this);
35127         }
35128         var cs = this.childNodes;
35129         for(var i = 0, len = cs.length; i < len; i++){
35130             cs[i].render(true);
35131         }
35132         this.childrenRendered = true;
35133     },
35134
35135     // private
35136     sort : function(fn, scope){
35137         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35138         if(this.childrenRendered){
35139             var cs = this.childNodes;
35140             for(var i = 0, len = cs.length; i < len; i++){
35141                 cs[i].render(true);
35142             }
35143         }
35144     },
35145
35146     // private
35147     render : function(bulkRender){
35148         this.ui.render(bulkRender);
35149         if(!this.rendered){
35150             this.rendered = true;
35151             if(this.expanded){
35152                 this.expanded = false;
35153                 this.expand(false, false);
35154             }
35155         }
35156     },
35157
35158     // private
35159     renderIndent : function(deep, refresh){
35160         if(refresh){
35161             this.ui.childIndent = null;
35162         }
35163         this.ui.renderIndent();
35164         if(deep === true && this.childrenRendered){
35165             var cs = this.childNodes;
35166             for(var i = 0, len = cs.length; i < len; i++){
35167                 cs[i].renderIndent(true, refresh);
35168             }
35169         }
35170     }
35171 });/*
35172  * Based on:
35173  * Ext JS Library 1.1.1
35174  * Copyright(c) 2006-2007, Ext JS, LLC.
35175  *
35176  * Originally Released Under LGPL - original licence link has changed is not relivant.
35177  *
35178  * Fork - LGPL
35179  * <script type="text/javascript">
35180  */
35181  
35182 /**
35183  * @class Roo.tree.AsyncTreeNode
35184  * @extends Roo.tree.TreeNode
35185  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35186  * @constructor
35187  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35188  */
35189  Roo.tree.AsyncTreeNode = function(config){
35190     this.loaded = false;
35191     this.loading = false;
35192     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35193     /**
35194     * @event beforeload
35195     * Fires before this node is loaded, return false to cancel
35196     * @param {Node} this This node
35197     */
35198     this.addEvents({'beforeload':true, 'load': true});
35199     /**
35200     * @event load
35201     * Fires when this node is loaded
35202     * @param {Node} this This node
35203     */
35204     /**
35205      * The loader used by this node (defaults to using the tree's defined loader)
35206      * @type TreeLoader
35207      * @property loader
35208      */
35209 };
35210 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35211     expand : function(deep, anim, callback){
35212         if(this.loading){ // if an async load is already running, waiting til it's done
35213             var timer;
35214             var f = function(){
35215                 if(!this.loading){ // done loading
35216                     clearInterval(timer);
35217                     this.expand(deep, anim, callback);
35218                 }
35219             }.createDelegate(this);
35220             timer = setInterval(f, 200);
35221             return;
35222         }
35223         if(!this.loaded){
35224             if(this.fireEvent("beforeload", this) === false){
35225                 return;
35226             }
35227             this.loading = true;
35228             this.ui.beforeLoad(this);
35229             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35230             if(loader){
35231                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35232                 return;
35233             }
35234         }
35235         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35236     },
35237     
35238     /**
35239      * Returns true if this node is currently loading
35240      * @return {Boolean}
35241      */
35242     isLoading : function(){
35243         return this.loading;  
35244     },
35245     
35246     loadComplete : function(deep, anim, callback){
35247         this.loading = false;
35248         this.loaded = true;
35249         this.ui.afterLoad(this);
35250         this.fireEvent("load", this);
35251         this.expand(deep, anim, callback);
35252     },
35253     
35254     /**
35255      * Returns true if this node has been loaded
35256      * @return {Boolean}
35257      */
35258     isLoaded : function(){
35259         return this.loaded;
35260     },
35261     
35262     hasChildNodes : function(){
35263         if(!this.isLeaf() && !this.loaded){
35264             return true;
35265         }else{
35266             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35267         }
35268     },
35269
35270     /**
35271      * Trigger a reload for this node
35272      * @param {Function} callback
35273      */
35274     reload : function(callback){
35275         this.collapse(false, false);
35276         while(this.firstChild){
35277             this.removeChild(this.firstChild);
35278         }
35279         this.childrenRendered = false;
35280         this.loaded = false;
35281         if(this.isHiddenRoot()){
35282             this.expanded = false;
35283         }
35284         this.expand(false, false, callback);
35285     }
35286 });/*
35287  * Based on:
35288  * Ext JS Library 1.1.1
35289  * Copyright(c) 2006-2007, Ext JS, LLC.
35290  *
35291  * Originally Released Under LGPL - original licence link has changed is not relivant.
35292  *
35293  * Fork - LGPL
35294  * <script type="text/javascript">
35295  */
35296  
35297 /**
35298  * @class Roo.tree.TreeNodeUI
35299  * @constructor
35300  * @param {Object} node The node to render
35301  * The TreeNode UI implementation is separate from the
35302  * tree implementation. Unless you are customizing the tree UI,
35303  * you should never have to use this directly.
35304  */
35305 Roo.tree.TreeNodeUI = function(node){
35306     this.node = node;
35307     this.rendered = false;
35308     this.animating = false;
35309     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35310 };
35311
35312 Roo.tree.TreeNodeUI.prototype = {
35313     removeChild : function(node){
35314         if(this.rendered){
35315             this.ctNode.removeChild(node.ui.getEl());
35316         }
35317     },
35318
35319     beforeLoad : function(){
35320          this.addClass("x-tree-node-loading");
35321     },
35322
35323     afterLoad : function(){
35324          this.removeClass("x-tree-node-loading");
35325     },
35326
35327     onTextChange : function(node, text, oldText){
35328         if(this.rendered){
35329             this.textNode.innerHTML = text;
35330         }
35331     },
35332
35333     onDisableChange : function(node, state){
35334         this.disabled = state;
35335         if(state){
35336             this.addClass("x-tree-node-disabled");
35337         }else{
35338             this.removeClass("x-tree-node-disabled");
35339         }
35340     },
35341
35342     onSelectedChange : function(state){
35343         if(state){
35344             this.focus();
35345             this.addClass("x-tree-selected");
35346         }else{
35347             //this.blur();
35348             this.removeClass("x-tree-selected");
35349         }
35350     },
35351
35352     onMove : function(tree, node, oldParent, newParent, index, refNode){
35353         this.childIndent = null;
35354         if(this.rendered){
35355             var targetNode = newParent.ui.getContainer();
35356             if(!targetNode){//target not rendered
35357                 this.holder = document.createElement("div");
35358                 this.holder.appendChild(this.wrap);
35359                 return;
35360             }
35361             var insertBefore = refNode ? refNode.ui.getEl() : null;
35362             if(insertBefore){
35363                 targetNode.insertBefore(this.wrap, insertBefore);
35364             }else{
35365                 targetNode.appendChild(this.wrap);
35366             }
35367             this.node.renderIndent(true);
35368         }
35369     },
35370
35371     addClass : function(cls){
35372         if(this.elNode){
35373             Roo.fly(this.elNode).addClass(cls);
35374         }
35375     },
35376
35377     removeClass : function(cls){
35378         if(this.elNode){
35379             Roo.fly(this.elNode).removeClass(cls);
35380         }
35381     },
35382
35383     remove : function(){
35384         if(this.rendered){
35385             this.holder = document.createElement("div");
35386             this.holder.appendChild(this.wrap);
35387         }
35388     },
35389
35390     fireEvent : function(){
35391         return this.node.fireEvent.apply(this.node, arguments);
35392     },
35393
35394     initEvents : function(){
35395         this.node.on("move", this.onMove, this);
35396         var E = Roo.EventManager;
35397         var a = this.anchor;
35398
35399         var el = Roo.fly(a, '_treeui');
35400
35401         if(Roo.isOpera){ // opera render bug ignores the CSS
35402             el.setStyle("text-decoration", "none");
35403         }
35404
35405         el.on("click", this.onClick, this);
35406         el.on("dblclick", this.onDblClick, this);
35407
35408         if(this.checkbox){
35409             Roo.EventManager.on(this.checkbox,
35410                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35411         }
35412
35413         el.on("contextmenu", this.onContextMenu, this);
35414
35415         var icon = Roo.fly(this.iconNode);
35416         icon.on("click", this.onClick, this);
35417         icon.on("dblclick", this.onDblClick, this);
35418         icon.on("contextmenu", this.onContextMenu, this);
35419         E.on(this.ecNode, "click", this.ecClick, this, true);
35420
35421         if(this.node.disabled){
35422             this.addClass("x-tree-node-disabled");
35423         }
35424         if(this.node.hidden){
35425             this.addClass("x-tree-node-disabled");
35426         }
35427         var ot = this.node.getOwnerTree();
35428         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35429         if(dd && (!this.node.isRoot || ot.rootVisible)){
35430             Roo.dd.Registry.register(this.elNode, {
35431                 node: this.node,
35432                 handles: this.getDDHandles(),
35433                 isHandle: false
35434             });
35435         }
35436     },
35437
35438     getDDHandles : function(){
35439         return [this.iconNode, this.textNode];
35440     },
35441
35442     hide : function(){
35443         if(this.rendered){
35444             this.wrap.style.display = "none";
35445         }
35446     },
35447
35448     show : function(){
35449         if(this.rendered){
35450             this.wrap.style.display = "";
35451         }
35452     },
35453
35454     onContextMenu : function(e){
35455         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35456             e.preventDefault();
35457             this.focus();
35458             this.fireEvent("contextmenu", this.node, e);
35459         }
35460     },
35461
35462     onClick : function(e){
35463         if(this.dropping){
35464             e.stopEvent();
35465             return;
35466         }
35467         if(this.fireEvent("beforeclick", this.node, e) !== false){
35468             if(!this.disabled && this.node.attributes.href){
35469                 this.fireEvent("click", this.node, e);
35470                 return;
35471             }
35472             e.preventDefault();
35473             if(this.disabled){
35474                 return;
35475             }
35476
35477             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35478                 this.node.toggle();
35479             }
35480
35481             this.fireEvent("click", this.node, e);
35482         }else{
35483             e.stopEvent();
35484         }
35485     },
35486
35487     onDblClick : function(e){
35488         e.preventDefault();
35489         if(this.disabled){
35490             return;
35491         }
35492         if(this.checkbox){
35493             this.toggleCheck();
35494         }
35495         if(!this.animating && this.node.hasChildNodes()){
35496             this.node.toggle();
35497         }
35498         this.fireEvent("dblclick", this.node, e);
35499     },
35500
35501     onCheckChange : function(){
35502         var checked = this.checkbox.checked;
35503         this.node.attributes.checked = checked;
35504         this.fireEvent('checkchange', this.node, checked);
35505     },
35506
35507     ecClick : function(e){
35508         if(!this.animating && this.node.hasChildNodes()){
35509             this.node.toggle();
35510         }
35511     },
35512
35513     startDrop : function(){
35514         this.dropping = true;
35515     },
35516
35517     // delayed drop so the click event doesn't get fired on a drop
35518     endDrop : function(){
35519        setTimeout(function(){
35520            this.dropping = false;
35521        }.createDelegate(this), 50);
35522     },
35523
35524     expand : function(){
35525         this.updateExpandIcon();
35526         this.ctNode.style.display = "";
35527     },
35528
35529     focus : function(){
35530         if(!this.node.preventHScroll){
35531             try{this.anchor.focus();
35532             }catch(e){}
35533         }else if(!Roo.isIE){
35534             try{
35535                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35536                 var l = noscroll.scrollLeft;
35537                 this.anchor.focus();
35538                 noscroll.scrollLeft = l;
35539             }catch(e){}
35540         }
35541     },
35542
35543     toggleCheck : function(value){
35544         var cb = this.checkbox;
35545         if(cb){
35546             cb.checked = (value === undefined ? !cb.checked : value);
35547         }
35548     },
35549
35550     blur : function(){
35551         try{
35552             this.anchor.blur();
35553         }catch(e){}
35554     },
35555
35556     animExpand : function(callback){
35557         var ct = Roo.get(this.ctNode);
35558         ct.stopFx();
35559         if(!this.node.hasChildNodes()){
35560             this.updateExpandIcon();
35561             this.ctNode.style.display = "";
35562             Roo.callback(callback);
35563             return;
35564         }
35565         this.animating = true;
35566         this.updateExpandIcon();
35567
35568         ct.slideIn('t', {
35569            callback : function(){
35570                this.animating = false;
35571                Roo.callback(callback);
35572             },
35573             scope: this,
35574             duration: this.node.ownerTree.duration || .25
35575         });
35576     },
35577
35578     highlight : function(){
35579         var tree = this.node.getOwnerTree();
35580         Roo.fly(this.wrap).highlight(
35581             tree.hlColor || "C3DAF9",
35582             {endColor: tree.hlBaseColor}
35583         );
35584     },
35585
35586     collapse : function(){
35587         this.updateExpandIcon();
35588         this.ctNode.style.display = "none";
35589     },
35590
35591     animCollapse : function(callback){
35592         var ct = Roo.get(this.ctNode);
35593         ct.enableDisplayMode('block');
35594         ct.stopFx();
35595
35596         this.animating = true;
35597         this.updateExpandIcon();
35598
35599         ct.slideOut('t', {
35600             callback : function(){
35601                this.animating = false;
35602                Roo.callback(callback);
35603             },
35604             scope: this,
35605             duration: this.node.ownerTree.duration || .25
35606         });
35607     },
35608
35609     getContainer : function(){
35610         return this.ctNode;
35611     },
35612
35613     getEl : function(){
35614         return this.wrap;
35615     },
35616
35617     appendDDGhost : function(ghostNode){
35618         ghostNode.appendChild(this.elNode.cloneNode(true));
35619     },
35620
35621     getDDRepairXY : function(){
35622         return Roo.lib.Dom.getXY(this.iconNode);
35623     },
35624
35625     onRender : function(){
35626         this.render();
35627     },
35628
35629     render : function(bulkRender){
35630         var n = this.node, a = n.attributes;
35631         var targetNode = n.parentNode ?
35632               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35633
35634         if(!this.rendered){
35635             this.rendered = true;
35636
35637             this.renderElements(n, a, targetNode, bulkRender);
35638
35639             if(a.qtip){
35640                if(this.textNode.setAttributeNS){
35641                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35642                    if(a.qtipTitle){
35643                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35644                    }
35645                }else{
35646                    this.textNode.setAttribute("ext:qtip", a.qtip);
35647                    if(a.qtipTitle){
35648                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35649                    }
35650                }
35651             }else if(a.qtipCfg){
35652                 a.qtipCfg.target = Roo.id(this.textNode);
35653                 Roo.QuickTips.register(a.qtipCfg);
35654             }
35655             this.initEvents();
35656             if(!this.node.expanded){
35657                 this.updateExpandIcon();
35658             }
35659         }else{
35660             if(bulkRender === true) {
35661                 targetNode.appendChild(this.wrap);
35662             }
35663         }
35664     },
35665
35666     renderElements : function(n, a, targetNode, bulkRender)
35667     {
35668         // add some indent caching, this helps performance when rendering a large tree
35669         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35670         var t = n.getOwnerTree();
35671         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35672         if (typeof(n.attributes.html) != 'undefined') {
35673             txt = n.attributes.html;
35674         }
35675         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35676         var cb = typeof a.checked == 'boolean';
35677         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35678         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35679             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35680             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35681             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35682             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35683             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35684              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35685                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35686             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35687             "</li>"];
35688
35689         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35690             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35691                                 n.nextSibling.ui.getEl(), buf.join(""));
35692         }else{
35693             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35694         }
35695
35696         this.elNode = this.wrap.childNodes[0];
35697         this.ctNode = this.wrap.childNodes[1];
35698         var cs = this.elNode.childNodes;
35699         this.indentNode = cs[0];
35700         this.ecNode = cs[1];
35701         this.iconNode = cs[2];
35702         var index = 3;
35703         if(cb){
35704             this.checkbox = cs[3];
35705             index++;
35706         }
35707         this.anchor = cs[index];
35708         this.textNode = cs[index].firstChild;
35709     },
35710
35711     getAnchor : function(){
35712         return this.anchor;
35713     },
35714
35715     getTextEl : function(){
35716         return this.textNode;
35717     },
35718
35719     getIconEl : function(){
35720         return this.iconNode;
35721     },
35722
35723     isChecked : function(){
35724         return this.checkbox ? this.checkbox.checked : false;
35725     },
35726
35727     updateExpandIcon : function(){
35728         if(this.rendered){
35729             var n = this.node, c1, c2;
35730             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35731             var hasChild = n.hasChildNodes();
35732             if(hasChild){
35733                 if(n.expanded){
35734                     cls += "-minus";
35735                     c1 = "x-tree-node-collapsed";
35736                     c2 = "x-tree-node-expanded";
35737                 }else{
35738                     cls += "-plus";
35739                     c1 = "x-tree-node-expanded";
35740                     c2 = "x-tree-node-collapsed";
35741                 }
35742                 if(this.wasLeaf){
35743                     this.removeClass("x-tree-node-leaf");
35744                     this.wasLeaf = false;
35745                 }
35746                 if(this.c1 != c1 || this.c2 != c2){
35747                     Roo.fly(this.elNode).replaceClass(c1, c2);
35748                     this.c1 = c1; this.c2 = c2;
35749                 }
35750             }else{
35751                 // this changes non-leafs into leafs if they have no children.
35752                 // it's not very rational behaviour..
35753                 
35754                 if(!this.wasLeaf && this.node.leaf){
35755                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35756                     delete this.c1;
35757                     delete this.c2;
35758                     this.wasLeaf = true;
35759                 }
35760             }
35761             var ecc = "x-tree-ec-icon "+cls;
35762             if(this.ecc != ecc){
35763                 this.ecNode.className = ecc;
35764                 this.ecc = ecc;
35765             }
35766         }
35767     },
35768
35769     getChildIndent : function(){
35770         if(!this.childIndent){
35771             var buf = [];
35772             var p = this.node;
35773             while(p){
35774                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35775                     if(!p.isLast()) {
35776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35777                     } else {
35778                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35779                     }
35780                 }
35781                 p = p.parentNode;
35782             }
35783             this.childIndent = buf.join("");
35784         }
35785         return this.childIndent;
35786     },
35787
35788     renderIndent : function(){
35789         if(this.rendered){
35790             var indent = "";
35791             var p = this.node.parentNode;
35792             if(p){
35793                 indent = p.ui.getChildIndent();
35794             }
35795             if(this.indentMarkup != indent){ // don't rerender if not required
35796                 this.indentNode.innerHTML = indent;
35797                 this.indentMarkup = indent;
35798             }
35799             this.updateExpandIcon();
35800         }
35801     }
35802 };
35803
35804 Roo.tree.RootTreeNodeUI = function(){
35805     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35806 };
35807 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35808     render : function(){
35809         if(!this.rendered){
35810             var targetNode = this.node.ownerTree.innerCt.dom;
35811             this.node.expanded = true;
35812             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35813             this.wrap = this.ctNode = targetNode.firstChild;
35814         }
35815     },
35816     collapse : function(){
35817     },
35818     expand : function(){
35819     }
35820 });/*
35821  * Based on:
35822  * Ext JS Library 1.1.1
35823  * Copyright(c) 2006-2007, Ext JS, LLC.
35824  *
35825  * Originally Released Under LGPL - original licence link has changed is not relivant.
35826  *
35827  * Fork - LGPL
35828  * <script type="text/javascript">
35829  */
35830 /**
35831  * @class Roo.tree.TreeLoader
35832  * @extends Roo.util.Observable
35833  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35834  * nodes from a specified URL. The response must be a javascript Array definition
35835  * who's elements are node definition objects. eg:
35836  * <pre><code>
35837 {  success : true,
35838    data :      [
35839    
35840     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35841     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35842     ]
35843 }
35844
35845
35846 </code></pre>
35847  * <br><br>
35848  * The old style respose with just an array is still supported, but not recommended.
35849  * <br><br>
35850  *
35851  * A server request is sent, and child nodes are loaded only when a node is expanded.
35852  * The loading node's id is passed to the server under the parameter name "node" to
35853  * enable the server to produce the correct child nodes.
35854  * <br><br>
35855  * To pass extra parameters, an event handler may be attached to the "beforeload"
35856  * event, and the parameters specified in the TreeLoader's baseParams property:
35857  * <pre><code>
35858     myTreeLoader.on("beforeload", function(treeLoader, node) {
35859         this.baseParams.category = node.attributes.category;
35860     }, this);
35861 </code></pre><
35862  * This would pass an HTTP parameter called "category" to the server containing
35863  * the value of the Node's "category" attribute.
35864  * @constructor
35865  * Creates a new Treeloader.
35866  * @param {Object} config A config object containing config properties.
35867  */
35868 Roo.tree.TreeLoader = function(config){
35869     this.baseParams = {};
35870     this.requestMethod = "POST";
35871     Roo.apply(this, config);
35872
35873     this.addEvents({
35874     
35875         /**
35876          * @event beforeload
35877          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35878          * @param {Object} This TreeLoader object.
35879          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35880          * @param {Object} callback The callback function specified in the {@link #load} call.
35881          */
35882         beforeload : true,
35883         /**
35884          * @event load
35885          * Fires when the node has been successfuly loaded.
35886          * @param {Object} This TreeLoader object.
35887          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35888          * @param {Object} response The response object containing the data from the server.
35889          */
35890         load : true,
35891         /**
35892          * @event loadexception
35893          * Fires if the network request failed.
35894          * @param {Object} This TreeLoader object.
35895          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35896          * @param {Object} response The response object containing the data from the server.
35897          */
35898         loadexception : true,
35899         /**
35900          * @event create
35901          * Fires before a node is created, enabling you to return custom Node types 
35902          * @param {Object} This TreeLoader object.
35903          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35904          */
35905         create : true
35906     });
35907
35908     Roo.tree.TreeLoader.superclass.constructor.call(this);
35909 };
35910
35911 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35912     /**
35913     * @cfg {String} dataUrl The URL from which to request a Json string which
35914     * specifies an array of node definition object representing the child nodes
35915     * to be loaded.
35916     */
35917     /**
35918     * @cfg {String} requestMethod either GET or POST
35919     * defaults to POST (due to BC)
35920     * to be loaded.
35921     */
35922     /**
35923     * @cfg {Object} baseParams (optional) An object containing properties which
35924     * specify HTTP parameters to be passed to each request for child nodes.
35925     */
35926     /**
35927     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35928     * created by this loader. If the attributes sent by the server have an attribute in this object,
35929     * they take priority.
35930     */
35931     /**
35932     * @cfg {Object} uiProviders (optional) An object containing properties which
35933     * 
35934     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35935     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35936     * <i>uiProvider</i> attribute of a returned child node is a string rather
35937     * than a reference to a TreeNodeUI implementation, this that string value
35938     * is used as a property name in the uiProviders object. You can define the provider named
35939     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35940     */
35941     uiProviders : {},
35942
35943     /**
35944     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35945     * child nodes before loading.
35946     */
35947     clearOnLoad : true,
35948
35949     /**
35950     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35951     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35952     * Grid query { data : [ .....] }
35953     */
35954     
35955     root : false,
35956      /**
35957     * @cfg {String} queryParam (optional) 
35958     * Name of the query as it will be passed on the querystring (defaults to 'node')
35959     * eg. the request will be ?node=[id]
35960     */
35961     
35962     
35963     queryParam: false,
35964     
35965     /**
35966      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35967      * This is called automatically when a node is expanded, but may be used to reload
35968      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35969      * @param {Roo.tree.TreeNode} node
35970      * @param {Function} callback
35971      */
35972     load : function(node, callback){
35973         if(this.clearOnLoad){
35974             while(node.firstChild){
35975                 node.removeChild(node.firstChild);
35976             }
35977         }
35978         if(node.attributes.children){ // preloaded json children
35979             var cs = node.attributes.children;
35980             for(var i = 0, len = cs.length; i < len; i++){
35981                 node.appendChild(this.createNode(cs[i]));
35982             }
35983             if(typeof callback == "function"){
35984                 callback();
35985             }
35986         }else if(this.dataUrl){
35987             this.requestData(node, callback);
35988         }
35989     },
35990
35991     getParams: function(node){
35992         var buf = [], bp = this.baseParams;
35993         for(var key in bp){
35994             if(typeof bp[key] != "function"){
35995                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35996             }
35997         }
35998         var n = this.queryParam === false ? 'node' : this.queryParam;
35999         buf.push(n + "=", encodeURIComponent(node.id));
36000         return buf.join("");
36001     },
36002
36003     requestData : function(node, callback){
36004         if(this.fireEvent("beforeload", this, node, callback) !== false){
36005             this.transId = Roo.Ajax.request({
36006                 method:this.requestMethod,
36007                 url: this.dataUrl||this.url,
36008                 success: this.handleResponse,
36009                 failure: this.handleFailure,
36010                 scope: this,
36011                 argument: {callback: callback, node: node},
36012                 params: this.getParams(node)
36013             });
36014         }else{
36015             // if the load is cancelled, make sure we notify
36016             // the node that we are done
36017             if(typeof callback == "function"){
36018                 callback();
36019             }
36020         }
36021     },
36022
36023     isLoading : function(){
36024         return this.transId ? true : false;
36025     },
36026
36027     abort : function(){
36028         if(this.isLoading()){
36029             Roo.Ajax.abort(this.transId);
36030         }
36031     },
36032
36033     // private
36034     createNode : function(attr)
36035     {
36036         // apply baseAttrs, nice idea Corey!
36037         if(this.baseAttrs){
36038             Roo.applyIf(attr, this.baseAttrs);
36039         }
36040         if(this.applyLoader !== false){
36041             attr.loader = this;
36042         }
36043         // uiProvider = depreciated..
36044         
36045         if(typeof(attr.uiProvider) == 'string'){
36046            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36047                 /**  eval:var:attr */ eval(attr.uiProvider);
36048         }
36049         if(typeof(this.uiProviders['default']) != 'undefined') {
36050             attr.uiProvider = this.uiProviders['default'];
36051         }
36052         
36053         this.fireEvent('create', this, attr);
36054         
36055         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36056         return(attr.leaf ?
36057                         new Roo.tree.TreeNode(attr) :
36058                         new Roo.tree.AsyncTreeNode(attr));
36059     },
36060
36061     processResponse : function(response, node, callback)
36062     {
36063         var json = response.responseText;
36064         try {
36065             
36066             var o = Roo.decode(json);
36067             
36068             if (this.root === false && typeof(o.success) != undefined) {
36069                 this.root = 'data'; // the default behaviour for list like data..
36070                 }
36071                 
36072             if (this.root !== false &&  !o.success) {
36073                 // it's a failure condition.
36074                 var a = response.argument;
36075                 this.fireEvent("loadexception", this, a.node, response);
36076                 Roo.log("Load failed - should have a handler really");
36077                 return;
36078             }
36079             
36080             
36081             
36082             if (this.root !== false) {
36083                  o = o[this.root];
36084             }
36085             
36086             for(var i = 0, len = o.length; i < len; i++){
36087                 var n = this.createNode(o[i]);
36088                 if(n){
36089                     node.appendChild(n);
36090                 }
36091             }
36092             if(typeof callback == "function"){
36093                 callback(this, node);
36094             }
36095         }catch(e){
36096             this.handleFailure(response);
36097         }
36098     },
36099
36100     handleResponse : function(response){
36101         this.transId = false;
36102         var a = response.argument;
36103         this.processResponse(response, a.node, a.callback);
36104         this.fireEvent("load", this, a.node, response);
36105     },
36106
36107     handleFailure : function(response)
36108     {
36109         // should handle failure better..
36110         this.transId = false;
36111         var a = response.argument;
36112         this.fireEvent("loadexception", this, a.node, response);
36113         if(typeof a.callback == "function"){
36114             a.callback(this, a.node);
36115         }
36116     }
36117 });/*
36118  * Based on:
36119  * Ext JS Library 1.1.1
36120  * Copyright(c) 2006-2007, Ext JS, LLC.
36121  *
36122  * Originally Released Under LGPL - original licence link has changed is not relivant.
36123  *
36124  * Fork - LGPL
36125  * <script type="text/javascript">
36126  */
36127
36128 /**
36129 * @class Roo.tree.TreeFilter
36130 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36131 * @param {TreePanel} tree
36132 * @param {Object} config (optional)
36133  */
36134 Roo.tree.TreeFilter = function(tree, config){
36135     this.tree = tree;
36136     this.filtered = {};
36137     Roo.apply(this, config);
36138 };
36139
36140 Roo.tree.TreeFilter.prototype = {
36141     clearBlank:false,
36142     reverse:false,
36143     autoClear:false,
36144     remove:false,
36145
36146      /**
36147      * Filter the data by a specific attribute.
36148      * @param {String/RegExp} value Either string that the attribute value
36149      * should start with or a RegExp to test against the attribute
36150      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36151      * @param {TreeNode} startNode (optional) The node to start the filter at.
36152      */
36153     filter : function(value, attr, startNode){
36154         attr = attr || "text";
36155         var f;
36156         if(typeof value == "string"){
36157             var vlen = value.length;
36158             // auto clear empty filter
36159             if(vlen == 0 && this.clearBlank){
36160                 this.clear();
36161                 return;
36162             }
36163             value = value.toLowerCase();
36164             f = function(n){
36165                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36166             };
36167         }else if(value.exec){ // regex?
36168             f = function(n){
36169                 return value.test(n.attributes[attr]);
36170             };
36171         }else{
36172             throw 'Illegal filter type, must be string or regex';
36173         }
36174         this.filterBy(f, null, startNode);
36175         },
36176
36177     /**
36178      * Filter by a function. The passed function will be called with each
36179      * node in the tree (or from the startNode). If the function returns true, the node is kept
36180      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36181      * @param {Function} fn The filter function
36182      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36183      */
36184     filterBy : function(fn, scope, startNode){
36185         startNode = startNode || this.tree.root;
36186         if(this.autoClear){
36187             this.clear();
36188         }
36189         var af = this.filtered, rv = this.reverse;
36190         var f = function(n){
36191             if(n == startNode){
36192                 return true;
36193             }
36194             if(af[n.id]){
36195                 return false;
36196             }
36197             var m = fn.call(scope || n, n);
36198             if(!m || rv){
36199                 af[n.id] = n;
36200                 n.ui.hide();
36201                 return false;
36202             }
36203             return true;
36204         };
36205         startNode.cascade(f);
36206         if(this.remove){
36207            for(var id in af){
36208                if(typeof id != "function"){
36209                    var n = af[id];
36210                    if(n && n.parentNode){
36211                        n.parentNode.removeChild(n);
36212                    }
36213                }
36214            }
36215         }
36216     },
36217
36218     /**
36219      * Clears the current filter. Note: with the "remove" option
36220      * set a filter cannot be cleared.
36221      */
36222     clear : function(){
36223         var t = this.tree;
36224         var af = this.filtered;
36225         for(var id in af){
36226             if(typeof id != "function"){
36227                 var n = af[id];
36228                 if(n){
36229                     n.ui.show();
36230                 }
36231             }
36232         }
36233         this.filtered = {};
36234     }
36235 };
36236 /*
36237  * Based on:
36238  * Ext JS Library 1.1.1
36239  * Copyright(c) 2006-2007, Ext JS, LLC.
36240  *
36241  * Originally Released Under LGPL - original licence link has changed is not relivant.
36242  *
36243  * Fork - LGPL
36244  * <script type="text/javascript">
36245  */
36246  
36247
36248 /**
36249  * @class Roo.tree.TreeSorter
36250  * Provides sorting of nodes in a TreePanel
36251  * 
36252  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36253  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36254  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36255  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36256  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36257  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36258  * @constructor
36259  * @param {TreePanel} tree
36260  * @param {Object} config
36261  */
36262 Roo.tree.TreeSorter = function(tree, config){
36263     Roo.apply(this, config);
36264     tree.on("beforechildrenrendered", this.doSort, this);
36265     tree.on("append", this.updateSort, this);
36266     tree.on("insert", this.updateSort, this);
36267     
36268     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36269     var p = this.property || "text";
36270     var sortType = this.sortType;
36271     var fs = this.folderSort;
36272     var cs = this.caseSensitive === true;
36273     var leafAttr = this.leafAttr || 'leaf';
36274
36275     this.sortFn = function(n1, n2){
36276         if(fs){
36277             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36278                 return 1;
36279             }
36280             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36281                 return -1;
36282             }
36283         }
36284         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36285         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36286         if(v1 < v2){
36287                         return dsc ? +1 : -1;
36288                 }else if(v1 > v2){
36289                         return dsc ? -1 : +1;
36290         }else{
36291                 return 0;
36292         }
36293     };
36294 };
36295
36296 Roo.tree.TreeSorter.prototype = {
36297     doSort : function(node){
36298         node.sort(this.sortFn);
36299     },
36300     
36301     compareNodes : function(n1, n2){
36302         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36303     },
36304     
36305     updateSort : function(tree, node){
36306         if(node.childrenRendered){
36307             this.doSort.defer(1, this, [node]);
36308         }
36309     }
36310 };/*
36311  * Based on:
36312  * Ext JS Library 1.1.1
36313  * Copyright(c) 2006-2007, Ext JS, LLC.
36314  *
36315  * Originally Released Under LGPL - original licence link has changed is not relivant.
36316  *
36317  * Fork - LGPL
36318  * <script type="text/javascript">
36319  */
36320
36321 if(Roo.dd.DropZone){
36322     
36323 Roo.tree.TreeDropZone = function(tree, config){
36324     this.allowParentInsert = false;
36325     this.allowContainerDrop = false;
36326     this.appendOnly = false;
36327     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36328     this.tree = tree;
36329     this.lastInsertClass = "x-tree-no-status";
36330     this.dragOverData = {};
36331 };
36332
36333 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36334     ddGroup : "TreeDD",
36335     scroll:  true,
36336     
36337     expandDelay : 1000,
36338     
36339     expandNode : function(node){
36340         if(node.hasChildNodes() && !node.isExpanded()){
36341             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36342         }
36343     },
36344     
36345     queueExpand : function(node){
36346         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36347     },
36348     
36349     cancelExpand : function(){
36350         if(this.expandProcId){
36351             clearTimeout(this.expandProcId);
36352             this.expandProcId = false;
36353         }
36354     },
36355     
36356     isValidDropPoint : function(n, pt, dd, e, data){
36357         if(!n || !data){ return false; }
36358         var targetNode = n.node;
36359         var dropNode = data.node;
36360         // default drop rules
36361         if(!(targetNode && targetNode.isTarget && pt)){
36362             return false;
36363         }
36364         if(pt == "append" && targetNode.allowChildren === false){
36365             return false;
36366         }
36367         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36368             return false;
36369         }
36370         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36371             return false;
36372         }
36373         // reuse the object
36374         var overEvent = this.dragOverData;
36375         overEvent.tree = this.tree;
36376         overEvent.target = targetNode;
36377         overEvent.data = data;
36378         overEvent.point = pt;
36379         overEvent.source = dd;
36380         overEvent.rawEvent = e;
36381         overEvent.dropNode = dropNode;
36382         overEvent.cancel = false;  
36383         var result = this.tree.fireEvent("nodedragover", overEvent);
36384         return overEvent.cancel === false && result !== false;
36385     },
36386     
36387     getDropPoint : function(e, n, dd)
36388     {
36389         var tn = n.node;
36390         if(tn.isRoot){
36391             return tn.allowChildren !== false ? "append" : false; // always append for root
36392         }
36393         var dragEl = n.ddel;
36394         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36395         var y = Roo.lib.Event.getPageY(e);
36396         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36397         
36398         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36399         var noAppend = tn.allowChildren === false;
36400         if(this.appendOnly || tn.parentNode.allowChildren === false){
36401             return noAppend ? false : "append";
36402         }
36403         var noBelow = false;
36404         if(!this.allowParentInsert){
36405             noBelow = tn.hasChildNodes() && tn.isExpanded();
36406         }
36407         var q = (b - t) / (noAppend ? 2 : 3);
36408         if(y >= t && y < (t + q)){
36409             return "above";
36410         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36411             return "below";
36412         }else{
36413             return "append";
36414         }
36415     },
36416     
36417     onNodeEnter : function(n, dd, e, data)
36418     {
36419         this.cancelExpand();
36420     },
36421     
36422     onNodeOver : function(n, dd, e, data)
36423     {
36424        
36425         var pt = this.getDropPoint(e, n, dd);
36426         var node = n.node;
36427         
36428         // auto node expand check
36429         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36430             this.queueExpand(node);
36431         }else if(pt != "append"){
36432             this.cancelExpand();
36433         }
36434         
36435         // set the insert point style on the target node
36436         var returnCls = this.dropNotAllowed;
36437         if(this.isValidDropPoint(n, pt, dd, e, data)){
36438            if(pt){
36439                var el = n.ddel;
36440                var cls;
36441                if(pt == "above"){
36442                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36443                    cls = "x-tree-drag-insert-above";
36444                }else if(pt == "below"){
36445                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36446                    cls = "x-tree-drag-insert-below";
36447                }else{
36448                    returnCls = "x-tree-drop-ok-append";
36449                    cls = "x-tree-drag-append";
36450                }
36451                if(this.lastInsertClass != cls){
36452                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36453                    this.lastInsertClass = cls;
36454                }
36455            }
36456        }
36457        return returnCls;
36458     },
36459     
36460     onNodeOut : function(n, dd, e, data){
36461         
36462         this.cancelExpand();
36463         this.removeDropIndicators(n);
36464     },
36465     
36466     onNodeDrop : function(n, dd, e, data){
36467         var point = this.getDropPoint(e, n, dd);
36468         var targetNode = n.node;
36469         targetNode.ui.startDrop();
36470         if(!this.isValidDropPoint(n, point, dd, e, data)){
36471             targetNode.ui.endDrop();
36472             return false;
36473         }
36474         // first try to find the drop node
36475         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36476         var dropEvent = {
36477             tree : this.tree,
36478             target: targetNode,
36479             data: data,
36480             point: point,
36481             source: dd,
36482             rawEvent: e,
36483             dropNode: dropNode,
36484             cancel: !dropNode   
36485         };
36486         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36487         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36488             targetNode.ui.endDrop();
36489             return false;
36490         }
36491         // allow target changing
36492         targetNode = dropEvent.target;
36493         if(point == "append" && !targetNode.isExpanded()){
36494             targetNode.expand(false, null, function(){
36495                 this.completeDrop(dropEvent);
36496             }.createDelegate(this));
36497         }else{
36498             this.completeDrop(dropEvent);
36499         }
36500         return true;
36501     },
36502     
36503     completeDrop : function(de){
36504         var ns = de.dropNode, p = de.point, t = de.target;
36505         if(!(ns instanceof Array)){
36506             ns = [ns];
36507         }
36508         var n;
36509         for(var i = 0, len = ns.length; i < len; i++){
36510             n = ns[i];
36511             if(p == "above"){
36512                 t.parentNode.insertBefore(n, t);
36513             }else if(p == "below"){
36514                 t.parentNode.insertBefore(n, t.nextSibling);
36515             }else{
36516                 t.appendChild(n);
36517             }
36518         }
36519         n.ui.focus();
36520         if(this.tree.hlDrop){
36521             n.ui.highlight();
36522         }
36523         t.ui.endDrop();
36524         this.tree.fireEvent("nodedrop", de);
36525     },
36526     
36527     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36528         if(this.tree.hlDrop){
36529             dropNode.ui.focus();
36530             dropNode.ui.highlight();
36531         }
36532         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36533     },
36534     
36535     getTree : function(){
36536         return this.tree;
36537     },
36538     
36539     removeDropIndicators : function(n){
36540         if(n && n.ddel){
36541             var el = n.ddel;
36542             Roo.fly(el).removeClass([
36543                     "x-tree-drag-insert-above",
36544                     "x-tree-drag-insert-below",
36545                     "x-tree-drag-append"]);
36546             this.lastInsertClass = "_noclass";
36547         }
36548     },
36549     
36550     beforeDragDrop : function(target, e, id){
36551         this.cancelExpand();
36552         return true;
36553     },
36554     
36555     afterRepair : function(data){
36556         if(data && Roo.enableFx){
36557             data.node.ui.highlight();
36558         }
36559         this.hideProxy();
36560     } 
36561     
36562 });
36563
36564 }
36565 /*
36566  * Based on:
36567  * Ext JS Library 1.1.1
36568  * Copyright(c) 2006-2007, Ext JS, LLC.
36569  *
36570  * Originally Released Under LGPL - original licence link has changed is not relivant.
36571  *
36572  * Fork - LGPL
36573  * <script type="text/javascript">
36574  */
36575  
36576
36577 if(Roo.dd.DragZone){
36578 Roo.tree.TreeDragZone = function(tree, config){
36579     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36580     this.tree = tree;
36581 };
36582
36583 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36584     ddGroup : "TreeDD",
36585    
36586     onBeforeDrag : function(data, e){
36587         var n = data.node;
36588         return n && n.draggable && !n.disabled;
36589     },
36590      
36591     
36592     onInitDrag : function(e){
36593         var data = this.dragData;
36594         this.tree.getSelectionModel().select(data.node);
36595         this.proxy.update("");
36596         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36597         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36598     },
36599     
36600     getRepairXY : function(e, data){
36601         return data.node.ui.getDDRepairXY();
36602     },
36603     
36604     onEndDrag : function(data, e){
36605         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36606         
36607         
36608     },
36609     
36610     onValidDrop : function(dd, e, id){
36611         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36612         this.hideProxy();
36613     },
36614     
36615     beforeInvalidDrop : function(e, id){
36616         // this scrolls the original position back into view
36617         var sm = this.tree.getSelectionModel();
36618         sm.clearSelections();
36619         sm.select(this.dragData.node);
36620     }
36621 });
36622 }/*
36623  * Based on:
36624  * Ext JS Library 1.1.1
36625  * Copyright(c) 2006-2007, Ext JS, LLC.
36626  *
36627  * Originally Released Under LGPL - original licence link has changed is not relivant.
36628  *
36629  * Fork - LGPL
36630  * <script type="text/javascript">
36631  */
36632 /**
36633  * @class Roo.tree.TreeEditor
36634  * @extends Roo.Editor
36635  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36636  * as the editor field.
36637  * @constructor
36638  * @param {Object} config (used to be the tree panel.)
36639  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36640  * 
36641  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36642  * @cfg {Roo.form.TextField|Object} field The field configuration
36643  *
36644  * 
36645  */
36646 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36647     var tree = config;
36648     var field;
36649     if (oldconfig) { // old style..
36650         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36651     } else {
36652         // new style..
36653         tree = config.tree;
36654         config.field = config.field  || {};
36655         config.field.xtype = 'TextField';
36656         field = Roo.factory(config.field, Roo.form);
36657     }
36658     config = config || {};
36659     
36660     
36661     this.addEvents({
36662         /**
36663          * @event beforenodeedit
36664          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36665          * false from the handler of this event.
36666          * @param {Editor} this
36667          * @param {Roo.tree.Node} node 
36668          */
36669         "beforenodeedit" : true
36670     });
36671     
36672     //Roo.log(config);
36673     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36674
36675     this.tree = tree;
36676
36677     tree.on('beforeclick', this.beforeNodeClick, this);
36678     tree.getTreeEl().on('mousedown', this.hide, this);
36679     this.on('complete', this.updateNode, this);
36680     this.on('beforestartedit', this.fitToTree, this);
36681     this.on('startedit', this.bindScroll, this, {delay:10});
36682     this.on('specialkey', this.onSpecialKey, this);
36683 };
36684
36685 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36686     /**
36687      * @cfg {String} alignment
36688      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36689      */
36690     alignment: "l-l",
36691     // inherit
36692     autoSize: false,
36693     /**
36694      * @cfg {Boolean} hideEl
36695      * True to hide the bound element while the editor is displayed (defaults to false)
36696      */
36697     hideEl : false,
36698     /**
36699      * @cfg {String} cls
36700      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36701      */
36702     cls: "x-small-editor x-tree-editor",
36703     /**
36704      * @cfg {Boolean} shim
36705      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36706      */
36707     shim:false,
36708     // inherit
36709     shadow:"frame",
36710     /**
36711      * @cfg {Number} maxWidth
36712      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36713      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36714      * scroll and client offsets into account prior to each edit.
36715      */
36716     maxWidth: 250,
36717
36718     editDelay : 350,
36719
36720     // private
36721     fitToTree : function(ed, el){
36722         var td = this.tree.getTreeEl().dom, nd = el.dom;
36723         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36724             td.scrollLeft = nd.offsetLeft;
36725         }
36726         var w = Math.min(
36727                 this.maxWidth,
36728                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36729         this.setSize(w, '');
36730         
36731         return this.fireEvent('beforenodeedit', this, this.editNode);
36732         
36733     },
36734
36735     // private
36736     triggerEdit : function(node){
36737         this.completeEdit();
36738         this.editNode = node;
36739         this.startEdit(node.ui.textNode, node.text);
36740     },
36741
36742     // private
36743     bindScroll : function(){
36744         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36745     },
36746
36747     // private
36748     beforeNodeClick : function(node, e){
36749         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36750         this.lastClick = new Date();
36751         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36752             e.stopEvent();
36753             this.triggerEdit(node);
36754             return false;
36755         }
36756         return true;
36757     },
36758
36759     // private
36760     updateNode : function(ed, value){
36761         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36762         this.editNode.setText(value);
36763     },
36764
36765     // private
36766     onHide : function(){
36767         Roo.tree.TreeEditor.superclass.onHide.call(this);
36768         if(this.editNode){
36769             this.editNode.ui.focus();
36770         }
36771     },
36772
36773     // private
36774     onSpecialKey : function(field, e){
36775         var k = e.getKey();
36776         if(k == e.ESC){
36777             e.stopEvent();
36778             this.cancelEdit();
36779         }else if(k == e.ENTER && !e.hasModifier()){
36780             e.stopEvent();
36781             this.completeEdit();
36782         }
36783     }
36784 });//<Script type="text/javascript">
36785 /*
36786  * Based on:
36787  * Ext JS Library 1.1.1
36788  * Copyright(c) 2006-2007, Ext JS, LLC.
36789  *
36790  * Originally Released Under LGPL - original licence link has changed is not relivant.
36791  *
36792  * Fork - LGPL
36793  * <script type="text/javascript">
36794  */
36795  
36796 /**
36797  * Not documented??? - probably should be...
36798  */
36799
36800 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36801     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36802     
36803     renderElements : function(n, a, targetNode, bulkRender){
36804         //consel.log("renderElements?");
36805         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36806
36807         var t = n.getOwnerTree();
36808         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36809         
36810         var cols = t.columns;
36811         var bw = t.borderWidth;
36812         var c = cols[0];
36813         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36814          var cb = typeof a.checked == "boolean";
36815         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36816         var colcls = 'x-t-' + tid + '-c0';
36817         var buf = [
36818             '<li class="x-tree-node">',
36819             
36820                 
36821                 '<div class="x-tree-node-el ', a.cls,'">',
36822                     // extran...
36823                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36824                 
36825                 
36826                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36827                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36828                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36829                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36830                            (a.iconCls ? ' '+a.iconCls : ''),
36831                            '" unselectable="on" />',
36832                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36833                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36834                              
36835                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36836                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36837                             '<span unselectable="on" qtip="' + tx + '">',
36838                              tx,
36839                              '</span></a>' ,
36840                     '</div>',
36841                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36842                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36843                  ];
36844         for(var i = 1, len = cols.length; i < len; i++){
36845             c = cols[i];
36846             colcls = 'x-t-' + tid + '-c' +i;
36847             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36848             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36849                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36850                       "</div>");
36851          }
36852          
36853          buf.push(
36854             '</a>',
36855             '<div class="x-clear"></div></div>',
36856             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36857             "</li>");
36858         
36859         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36860             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36861                                 n.nextSibling.ui.getEl(), buf.join(""));
36862         }else{
36863             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36864         }
36865         var el = this.wrap.firstChild;
36866         this.elRow = el;
36867         this.elNode = el.firstChild;
36868         this.ranchor = el.childNodes[1];
36869         this.ctNode = this.wrap.childNodes[1];
36870         var cs = el.firstChild.childNodes;
36871         this.indentNode = cs[0];
36872         this.ecNode = cs[1];
36873         this.iconNode = cs[2];
36874         var index = 3;
36875         if(cb){
36876             this.checkbox = cs[3];
36877             index++;
36878         }
36879         this.anchor = cs[index];
36880         
36881         this.textNode = cs[index].firstChild;
36882         
36883         //el.on("click", this.onClick, this);
36884         //el.on("dblclick", this.onDblClick, this);
36885         
36886         
36887        // console.log(this);
36888     },
36889     initEvents : function(){
36890         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36891         
36892             
36893         var a = this.ranchor;
36894
36895         var el = Roo.get(a);
36896
36897         if(Roo.isOpera){ // opera render bug ignores the CSS
36898             el.setStyle("text-decoration", "none");
36899         }
36900
36901         el.on("click", this.onClick, this);
36902         el.on("dblclick", this.onDblClick, this);
36903         el.on("contextmenu", this.onContextMenu, this);
36904         
36905     },
36906     
36907     /*onSelectedChange : function(state){
36908         if(state){
36909             this.focus();
36910             this.addClass("x-tree-selected");
36911         }else{
36912             //this.blur();
36913             this.removeClass("x-tree-selected");
36914         }
36915     },*/
36916     addClass : function(cls){
36917         if(this.elRow){
36918             Roo.fly(this.elRow).addClass(cls);
36919         }
36920         
36921     },
36922     
36923     
36924     removeClass : function(cls){
36925         if(this.elRow){
36926             Roo.fly(this.elRow).removeClass(cls);
36927         }
36928     }
36929
36930     
36931     
36932 });//<Script type="text/javascript">
36933
36934 /*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944  
36945
36946 /**
36947  * @class Roo.tree.ColumnTree
36948  * @extends Roo.data.TreePanel
36949  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36950  * @cfg {int} borderWidth  compined right/left border allowance
36951  * @constructor
36952  * @param {String/HTMLElement/Element} el The container element
36953  * @param {Object} config
36954  */
36955 Roo.tree.ColumnTree =  function(el, config)
36956 {
36957    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36958    this.addEvents({
36959         /**
36960         * @event resize
36961         * Fire this event on a container when it resizes
36962         * @param {int} w Width
36963         * @param {int} h Height
36964         */
36965        "resize" : true
36966     });
36967     this.on('resize', this.onResize, this);
36968 };
36969
36970 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36971     //lines:false,
36972     
36973     
36974     borderWidth: Roo.isBorderBox ? 0 : 2, 
36975     headEls : false,
36976     
36977     render : function(){
36978         // add the header.....
36979        
36980         Roo.tree.ColumnTree.superclass.render.apply(this);
36981         
36982         this.el.addClass('x-column-tree');
36983         
36984         this.headers = this.el.createChild(
36985             {cls:'x-tree-headers'},this.innerCt.dom);
36986    
36987         var cols = this.columns, c;
36988         var totalWidth = 0;
36989         this.headEls = [];
36990         var  len = cols.length;
36991         for(var i = 0; i < len; i++){
36992              c = cols[i];
36993              totalWidth += c.width;
36994             this.headEls.push(this.headers.createChild({
36995                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36996                  cn: {
36997                      cls:'x-tree-hd-text',
36998                      html: c.header
36999                  },
37000                  style:'width:'+(c.width-this.borderWidth)+'px;'
37001              }));
37002         }
37003         this.headers.createChild({cls:'x-clear'});
37004         // prevent floats from wrapping when clipped
37005         this.headers.setWidth(totalWidth);
37006         //this.innerCt.setWidth(totalWidth);
37007         this.innerCt.setStyle({ overflow: 'auto' });
37008         this.onResize(this.width, this.height);
37009              
37010         
37011     },
37012     onResize : function(w,h)
37013     {
37014         this.height = h;
37015         this.width = w;
37016         // resize cols..
37017         this.innerCt.setWidth(this.width);
37018         this.innerCt.setHeight(this.height-20);
37019         
37020         // headers...
37021         var cols = this.columns, c;
37022         var totalWidth = 0;
37023         var expEl = false;
37024         var len = cols.length;
37025         for(var i = 0; i < len; i++){
37026             c = cols[i];
37027             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37028                 // it's the expander..
37029                 expEl  = this.headEls[i];
37030                 continue;
37031             }
37032             totalWidth += c.width;
37033             
37034         }
37035         if (expEl) {
37036             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37037         }
37038         this.headers.setWidth(w-20);
37039
37040         
37041         
37042         
37043     }
37044 });
37045 /*
37046  * Based on:
37047  * Ext JS Library 1.1.1
37048  * Copyright(c) 2006-2007, Ext JS, LLC.
37049  *
37050  * Originally Released Under LGPL - original licence link has changed is not relivant.
37051  *
37052  * Fork - LGPL
37053  * <script type="text/javascript">
37054  */
37055  
37056 /**
37057  * @class Roo.menu.Menu
37058  * @extends Roo.util.Observable
37059  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37060  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37061  * @constructor
37062  * Creates a new Menu
37063  * @param {Object} config Configuration options
37064  */
37065 Roo.menu.Menu = function(config){
37066     Roo.apply(this, config);
37067     this.id = this.id || Roo.id();
37068     this.addEvents({
37069         /**
37070          * @event beforeshow
37071          * Fires before this menu is displayed
37072          * @param {Roo.menu.Menu} this
37073          */
37074         beforeshow : true,
37075         /**
37076          * @event beforehide
37077          * Fires before this menu is hidden
37078          * @param {Roo.menu.Menu} this
37079          */
37080         beforehide : true,
37081         /**
37082          * @event show
37083          * Fires after this menu is displayed
37084          * @param {Roo.menu.Menu} this
37085          */
37086         show : true,
37087         /**
37088          * @event hide
37089          * Fires after this menu is hidden
37090          * @param {Roo.menu.Menu} this
37091          */
37092         hide : true,
37093         /**
37094          * @event click
37095          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37096          * @param {Roo.menu.Menu} this
37097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37098          * @param {Roo.EventObject} e
37099          */
37100         click : true,
37101         /**
37102          * @event mouseover
37103          * Fires when the mouse is hovering over this menu
37104          * @param {Roo.menu.Menu} this
37105          * @param {Roo.EventObject} e
37106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37107          */
37108         mouseover : true,
37109         /**
37110          * @event mouseout
37111          * Fires when the mouse exits this menu
37112          * @param {Roo.menu.Menu} this
37113          * @param {Roo.EventObject} e
37114          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37115          */
37116         mouseout : true,
37117         /**
37118          * @event itemclick
37119          * Fires when a menu item contained in this menu is clicked
37120          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37121          * @param {Roo.EventObject} e
37122          */
37123         itemclick: true
37124     });
37125     if (this.registerMenu) {
37126         Roo.menu.MenuMgr.register(this);
37127     }
37128     
37129     var mis = this.items;
37130     this.items = new Roo.util.MixedCollection();
37131     if(mis){
37132         this.add.apply(this, mis);
37133     }
37134 };
37135
37136 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37137     /**
37138      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37139      */
37140     minWidth : 120,
37141     /**
37142      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37143      * for bottom-right shadow (defaults to "sides")
37144      */
37145     shadow : "sides",
37146     /**
37147      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37148      * this menu (defaults to "tl-tr?")
37149      */
37150     subMenuAlign : "tl-tr?",
37151     /**
37152      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37153      * relative to its element of origin (defaults to "tl-bl?")
37154      */
37155     defaultAlign : "tl-bl?",
37156     /**
37157      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37158      */
37159     allowOtherMenus : false,
37160     /**
37161      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37162      */
37163     registerMenu : true,
37164
37165     hidden:true,
37166
37167     // private
37168     render : function(){
37169         if(this.el){
37170             return;
37171         }
37172         var el = this.el = new Roo.Layer({
37173             cls: "x-menu",
37174             shadow:this.shadow,
37175             constrain: false,
37176             parentEl: this.parentEl || document.body,
37177             zindex:15000
37178         });
37179
37180         this.keyNav = new Roo.menu.MenuNav(this);
37181
37182         if(this.plain){
37183             el.addClass("x-menu-plain");
37184         }
37185         if(this.cls){
37186             el.addClass(this.cls);
37187         }
37188         // generic focus element
37189         this.focusEl = el.createChild({
37190             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37191         });
37192         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37193         //disabling touch- as it's causing issues ..
37194         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37195         ul.on('click'   , this.onClick, this);
37196         
37197         
37198         ul.on("mouseover", this.onMouseOver, this);
37199         ul.on("mouseout", this.onMouseOut, this);
37200         this.items.each(function(item){
37201             if (item.hidden) {
37202                 return;
37203             }
37204             
37205             var li = document.createElement("li");
37206             li.className = "x-menu-list-item";
37207             ul.dom.appendChild(li);
37208             item.render(li, this);
37209         }, this);
37210         this.ul = ul;
37211         this.autoWidth();
37212     },
37213
37214     // private
37215     autoWidth : function(){
37216         var el = this.el, ul = this.ul;
37217         if(!el){
37218             return;
37219         }
37220         var w = this.width;
37221         if(w){
37222             el.setWidth(w);
37223         }else if(Roo.isIE){
37224             el.setWidth(this.minWidth);
37225             var t = el.dom.offsetWidth; // force recalc
37226             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37227         }
37228     },
37229
37230     // private
37231     delayAutoWidth : function(){
37232         if(this.rendered){
37233             if(!this.awTask){
37234                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37235             }
37236             this.awTask.delay(20);
37237         }
37238     },
37239
37240     // private
37241     findTargetItem : function(e){
37242         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37243         if(t && t.menuItemId){
37244             return this.items.get(t.menuItemId);
37245         }
37246     },
37247
37248     // private
37249     onClick : function(e){
37250         Roo.log("menu.onClick");
37251         var t = this.findTargetItem(e);
37252         if(!t){
37253             return;
37254         }
37255         Roo.log(e);
37256         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37257             if(t == this.activeItem && t.shouldDeactivate(e)){
37258                 this.activeItem.deactivate();
37259                 delete this.activeItem;
37260                 return;
37261             }
37262             if(t.canActivate){
37263                 this.setActiveItem(t, true);
37264             }
37265             return;
37266             
37267             
37268         }
37269         
37270         t.onClick(e);
37271         this.fireEvent("click", this, t, e);
37272     },
37273
37274     // private
37275     setActiveItem : function(item, autoExpand){
37276         if(item != this.activeItem){
37277             if(this.activeItem){
37278                 this.activeItem.deactivate();
37279             }
37280             this.activeItem = item;
37281             item.activate(autoExpand);
37282         }else if(autoExpand){
37283             item.expandMenu();
37284         }
37285     },
37286
37287     // private
37288     tryActivate : function(start, step){
37289         var items = this.items;
37290         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37291             var item = items.get(i);
37292             if(!item.disabled && item.canActivate){
37293                 this.setActiveItem(item, false);
37294                 return item;
37295             }
37296         }
37297         return false;
37298     },
37299
37300     // private
37301     onMouseOver : function(e){
37302         var t;
37303         if(t = this.findTargetItem(e)){
37304             if(t.canActivate && !t.disabled){
37305                 this.setActiveItem(t, true);
37306             }
37307         }
37308         this.fireEvent("mouseover", this, e, t);
37309     },
37310
37311     // private
37312     onMouseOut : function(e){
37313         var t;
37314         if(t = this.findTargetItem(e)){
37315             if(t == this.activeItem && t.shouldDeactivate(e)){
37316                 this.activeItem.deactivate();
37317                 delete this.activeItem;
37318             }
37319         }
37320         this.fireEvent("mouseout", this, e, t);
37321     },
37322
37323     /**
37324      * Read-only.  Returns true if the menu is currently displayed, else false.
37325      * @type Boolean
37326      */
37327     isVisible : function(){
37328         return this.el && !this.hidden;
37329     },
37330
37331     /**
37332      * Displays this menu relative to another element
37333      * @param {String/HTMLElement/Roo.Element} element The element to align to
37334      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37335      * the element (defaults to this.defaultAlign)
37336      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37337      */
37338     show : function(el, pos, parentMenu){
37339         this.parentMenu = parentMenu;
37340         if(!this.el){
37341             this.render();
37342         }
37343         this.fireEvent("beforeshow", this);
37344         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37345     },
37346
37347     /**
37348      * Displays this menu at a specific xy position
37349      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37350      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37351      */
37352     showAt : function(xy, parentMenu, /* private: */_e){
37353         this.parentMenu = parentMenu;
37354         if(!this.el){
37355             this.render();
37356         }
37357         if(_e !== false){
37358             this.fireEvent("beforeshow", this);
37359             xy = this.el.adjustForConstraints(xy);
37360         }
37361         this.el.setXY(xy);
37362         this.el.show();
37363         this.hidden = false;
37364         this.focus();
37365         this.fireEvent("show", this);
37366     },
37367
37368     focus : function(){
37369         if(!this.hidden){
37370             this.doFocus.defer(50, this);
37371         }
37372     },
37373
37374     doFocus : function(){
37375         if(!this.hidden){
37376             this.focusEl.focus();
37377         }
37378     },
37379
37380     /**
37381      * Hides this menu and optionally all parent menus
37382      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37383      */
37384     hide : function(deep){
37385         if(this.el && this.isVisible()){
37386             this.fireEvent("beforehide", this);
37387             if(this.activeItem){
37388                 this.activeItem.deactivate();
37389                 this.activeItem = null;
37390             }
37391             this.el.hide();
37392             this.hidden = true;
37393             this.fireEvent("hide", this);
37394         }
37395         if(deep === true && this.parentMenu){
37396             this.parentMenu.hide(true);
37397         }
37398     },
37399
37400     /**
37401      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37402      * Any of the following are valid:
37403      * <ul>
37404      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37405      * <li>An HTMLElement object which will be converted to a menu item</li>
37406      * <li>A menu item config object that will be created as a new menu item</li>
37407      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37408      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37409      * </ul>
37410      * Usage:
37411      * <pre><code>
37412 // Create the menu
37413 var menu = new Roo.menu.Menu();
37414
37415 // Create a menu item to add by reference
37416 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37417
37418 // Add a bunch of items at once using different methods.
37419 // Only the last item added will be returned.
37420 var item = menu.add(
37421     menuItem,                // add existing item by ref
37422     'Dynamic Item',          // new TextItem
37423     '-',                     // new separator
37424     { text: 'Config Item' }  // new item by config
37425 );
37426 </code></pre>
37427      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37428      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37429      */
37430     add : function(){
37431         var a = arguments, l = a.length, item;
37432         for(var i = 0; i < l; i++){
37433             var el = a[i];
37434             if ((typeof(el) == "object") && el.xtype && el.xns) {
37435                 el = Roo.factory(el, Roo.menu);
37436             }
37437             
37438             if(el.render){ // some kind of Item
37439                 item = this.addItem(el);
37440             }else if(typeof el == "string"){ // string
37441                 if(el == "separator" || el == "-"){
37442                     item = this.addSeparator();
37443                 }else{
37444                     item = this.addText(el);
37445                 }
37446             }else if(el.tagName || el.el){ // element
37447                 item = this.addElement(el);
37448             }else if(typeof el == "object"){ // must be menu item config?
37449                 item = this.addMenuItem(el);
37450             }
37451         }
37452         return item;
37453     },
37454
37455     /**
37456      * Returns this menu's underlying {@link Roo.Element} object
37457      * @return {Roo.Element} The element
37458      */
37459     getEl : function(){
37460         if(!this.el){
37461             this.render();
37462         }
37463         return this.el;
37464     },
37465
37466     /**
37467      * Adds a separator bar to the menu
37468      * @return {Roo.menu.Item} The menu item that was added
37469      */
37470     addSeparator : function(){
37471         return this.addItem(new Roo.menu.Separator());
37472     },
37473
37474     /**
37475      * Adds an {@link Roo.Element} object to the menu
37476      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37477      * @return {Roo.menu.Item} The menu item that was added
37478      */
37479     addElement : function(el){
37480         return this.addItem(new Roo.menu.BaseItem(el));
37481     },
37482
37483     /**
37484      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37485      * @param {Roo.menu.Item} item The menu item to add
37486      * @return {Roo.menu.Item} The menu item that was added
37487      */
37488     addItem : function(item){
37489         this.items.add(item);
37490         if(this.ul){
37491             var li = document.createElement("li");
37492             li.className = "x-menu-list-item";
37493             this.ul.dom.appendChild(li);
37494             item.render(li, this);
37495             this.delayAutoWidth();
37496         }
37497         return item;
37498     },
37499
37500     /**
37501      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37502      * @param {Object} config A MenuItem config object
37503      * @return {Roo.menu.Item} The menu item that was added
37504      */
37505     addMenuItem : function(config){
37506         if(!(config instanceof Roo.menu.Item)){
37507             if(typeof config.checked == "boolean"){ // must be check menu item config?
37508                 config = new Roo.menu.CheckItem(config);
37509             }else{
37510                 config = new Roo.menu.Item(config);
37511             }
37512         }
37513         return this.addItem(config);
37514     },
37515
37516     /**
37517      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37518      * @param {String} text The text to display in the menu item
37519      * @return {Roo.menu.Item} The menu item that was added
37520      */
37521     addText : function(text){
37522         return this.addItem(new Roo.menu.TextItem({ text : text }));
37523     },
37524
37525     /**
37526      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37527      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37528      * @param {Roo.menu.Item} item The menu item to add
37529      * @return {Roo.menu.Item} The menu item that was added
37530      */
37531     insert : function(index, item){
37532         this.items.insert(index, item);
37533         if(this.ul){
37534             var li = document.createElement("li");
37535             li.className = "x-menu-list-item";
37536             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37537             item.render(li, this);
37538             this.delayAutoWidth();
37539         }
37540         return item;
37541     },
37542
37543     /**
37544      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37545      * @param {Roo.menu.Item} item The menu item to remove
37546      */
37547     remove : function(item){
37548         this.items.removeKey(item.id);
37549         item.destroy();
37550     },
37551
37552     /**
37553      * Removes and destroys all items in the menu
37554      */
37555     removeAll : function(){
37556         var f;
37557         while(f = this.items.first()){
37558             this.remove(f);
37559         }
37560     }
37561 });
37562
37563 // MenuNav is a private utility class used internally by the Menu
37564 Roo.menu.MenuNav = function(menu){
37565     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37566     this.scope = this.menu = menu;
37567 };
37568
37569 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37570     doRelay : function(e, h){
37571         var k = e.getKey();
37572         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37573             this.menu.tryActivate(0, 1);
37574             return false;
37575         }
37576         return h.call(this.scope || this, e, this.menu);
37577     },
37578
37579     up : function(e, m){
37580         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37581             m.tryActivate(m.items.length-1, -1);
37582         }
37583     },
37584
37585     down : function(e, m){
37586         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37587             m.tryActivate(0, 1);
37588         }
37589     },
37590
37591     right : function(e, m){
37592         if(m.activeItem){
37593             m.activeItem.expandMenu(true);
37594         }
37595     },
37596
37597     left : function(e, m){
37598         m.hide();
37599         if(m.parentMenu && m.parentMenu.activeItem){
37600             m.parentMenu.activeItem.activate();
37601         }
37602     },
37603
37604     enter : function(e, m){
37605         if(m.activeItem){
37606             e.stopPropagation();
37607             m.activeItem.onClick(e);
37608             m.fireEvent("click", this, m.activeItem);
37609             return true;
37610         }
37611     }
37612 });/*
37613  * Based on:
37614  * Ext JS Library 1.1.1
37615  * Copyright(c) 2006-2007, Ext JS, LLC.
37616  *
37617  * Originally Released Under LGPL - original licence link has changed is not relivant.
37618  *
37619  * Fork - LGPL
37620  * <script type="text/javascript">
37621  */
37622  
37623 /**
37624  * @class Roo.menu.MenuMgr
37625  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37626  * @singleton
37627  */
37628 Roo.menu.MenuMgr = function(){
37629    var menus, active, groups = {}, attached = false, lastShow = new Date();
37630
37631    // private - called when first menu is created
37632    function init(){
37633        menus = {};
37634        active = new Roo.util.MixedCollection();
37635        Roo.get(document).addKeyListener(27, function(){
37636            if(active.length > 0){
37637                hideAll();
37638            }
37639        });
37640    }
37641
37642    // private
37643    function hideAll(){
37644        if(active && active.length > 0){
37645            var c = active.clone();
37646            c.each(function(m){
37647                m.hide();
37648            });
37649        }
37650    }
37651
37652    // private
37653    function onHide(m){
37654        active.remove(m);
37655        if(active.length < 1){
37656            Roo.get(document).un("mousedown", onMouseDown);
37657            attached = false;
37658        }
37659    }
37660
37661    // private
37662    function onShow(m){
37663        var last = active.last();
37664        lastShow = new Date();
37665        active.add(m);
37666        if(!attached){
37667            Roo.get(document).on("mousedown", onMouseDown);
37668            attached = true;
37669        }
37670        if(m.parentMenu){
37671           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37672           m.parentMenu.activeChild = m;
37673        }else if(last && last.isVisible()){
37674           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37675        }
37676    }
37677
37678    // private
37679    function onBeforeHide(m){
37680        if(m.activeChild){
37681            m.activeChild.hide();
37682        }
37683        if(m.autoHideTimer){
37684            clearTimeout(m.autoHideTimer);
37685            delete m.autoHideTimer;
37686        }
37687    }
37688
37689    // private
37690    function onBeforeShow(m){
37691        var pm = m.parentMenu;
37692        if(!pm && !m.allowOtherMenus){
37693            hideAll();
37694        }else if(pm && pm.activeChild && active != m){
37695            pm.activeChild.hide();
37696        }
37697    }
37698
37699    // private
37700    function onMouseDown(e){
37701        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37702            hideAll();
37703        }
37704    }
37705
37706    // private
37707    function onBeforeCheck(mi, state){
37708        if(state){
37709            var g = groups[mi.group];
37710            for(var i = 0, l = g.length; i < l; i++){
37711                if(g[i] != mi){
37712                    g[i].setChecked(false);
37713                }
37714            }
37715        }
37716    }
37717
37718    return {
37719
37720        /**
37721         * Hides all menus that are currently visible
37722         */
37723        hideAll : function(){
37724             hideAll();  
37725        },
37726
37727        // private
37728        register : function(menu){
37729            if(!menus){
37730                init();
37731            }
37732            menus[menu.id] = menu;
37733            menu.on("beforehide", onBeforeHide);
37734            menu.on("hide", onHide);
37735            menu.on("beforeshow", onBeforeShow);
37736            menu.on("show", onShow);
37737            var g = menu.group;
37738            if(g && menu.events["checkchange"]){
37739                if(!groups[g]){
37740                    groups[g] = [];
37741                }
37742                groups[g].push(menu);
37743                menu.on("checkchange", onCheck);
37744            }
37745        },
37746
37747         /**
37748          * Returns a {@link Roo.menu.Menu} object
37749          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37750          * be used to generate and return a new Menu instance.
37751          */
37752        get : function(menu){
37753            if(typeof menu == "string"){ // menu id
37754                return menus[menu];
37755            }else if(menu.events){  // menu instance
37756                return menu;
37757            }else if(typeof menu.length == 'number'){ // array of menu items?
37758                return new Roo.menu.Menu({items:menu});
37759            }else{ // otherwise, must be a config
37760                return new Roo.menu.Menu(menu);
37761            }
37762        },
37763
37764        // private
37765        unregister : function(menu){
37766            delete menus[menu.id];
37767            menu.un("beforehide", onBeforeHide);
37768            menu.un("hide", onHide);
37769            menu.un("beforeshow", onBeforeShow);
37770            menu.un("show", onShow);
37771            var g = menu.group;
37772            if(g && menu.events["checkchange"]){
37773                groups[g].remove(menu);
37774                menu.un("checkchange", onCheck);
37775            }
37776        },
37777
37778        // private
37779        registerCheckable : function(menuItem){
37780            var g = menuItem.group;
37781            if(g){
37782                if(!groups[g]){
37783                    groups[g] = [];
37784                }
37785                groups[g].push(menuItem);
37786                menuItem.on("beforecheckchange", onBeforeCheck);
37787            }
37788        },
37789
37790        // private
37791        unregisterCheckable : function(menuItem){
37792            var g = menuItem.group;
37793            if(g){
37794                groups[g].remove(menuItem);
37795                menuItem.un("beforecheckchange", onBeforeCheck);
37796            }
37797        }
37798    };
37799 }();/*
37800  * Based on:
37801  * Ext JS Library 1.1.1
37802  * Copyright(c) 2006-2007, Ext JS, LLC.
37803  *
37804  * Originally Released Under LGPL - original licence link has changed is not relivant.
37805  *
37806  * Fork - LGPL
37807  * <script type="text/javascript">
37808  */
37809  
37810
37811 /**
37812  * @class Roo.menu.BaseItem
37813  * @extends Roo.Component
37814  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37815  * management and base configuration options shared by all menu components.
37816  * @constructor
37817  * Creates a new BaseItem
37818  * @param {Object} config Configuration options
37819  */
37820 Roo.menu.BaseItem = function(config){
37821     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37822
37823     this.addEvents({
37824         /**
37825          * @event click
37826          * Fires when this item is clicked
37827          * @param {Roo.menu.BaseItem} this
37828          * @param {Roo.EventObject} e
37829          */
37830         click: true,
37831         /**
37832          * @event activate
37833          * Fires when this item is activated
37834          * @param {Roo.menu.BaseItem} this
37835          */
37836         activate : true,
37837         /**
37838          * @event deactivate
37839          * Fires when this item is deactivated
37840          * @param {Roo.menu.BaseItem} this
37841          */
37842         deactivate : true
37843     });
37844
37845     if(this.handler){
37846         this.on("click", this.handler, this.scope, true);
37847     }
37848 };
37849
37850 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37851     /**
37852      * @cfg {Function} handler
37853      * A function that will handle the click event of this menu item (defaults to undefined)
37854      */
37855     /**
37856      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37857      */
37858     canActivate : false,
37859     
37860      /**
37861      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37862      */
37863     hidden: false,
37864     
37865     /**
37866      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37867      */
37868     activeClass : "x-menu-item-active",
37869     /**
37870      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37871      */
37872     hideOnClick : true,
37873     /**
37874      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37875      */
37876     hideDelay : 100,
37877
37878     // private
37879     ctype: "Roo.menu.BaseItem",
37880
37881     // private
37882     actionMode : "container",
37883
37884     // private
37885     render : function(container, parentMenu){
37886         this.parentMenu = parentMenu;
37887         Roo.menu.BaseItem.superclass.render.call(this, container);
37888         this.container.menuItemId = this.id;
37889     },
37890
37891     // private
37892     onRender : function(container, position){
37893         this.el = Roo.get(this.el);
37894         container.dom.appendChild(this.el.dom);
37895     },
37896
37897     // private
37898     onClick : function(e){
37899         if(!this.disabled && this.fireEvent("click", this, e) !== false
37900                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37901             this.handleClick(e);
37902         }else{
37903             e.stopEvent();
37904         }
37905     },
37906
37907     // private
37908     activate : function(){
37909         if(this.disabled){
37910             return false;
37911         }
37912         var li = this.container;
37913         li.addClass(this.activeClass);
37914         this.region = li.getRegion().adjust(2, 2, -2, -2);
37915         this.fireEvent("activate", this);
37916         return true;
37917     },
37918
37919     // private
37920     deactivate : function(){
37921         this.container.removeClass(this.activeClass);
37922         this.fireEvent("deactivate", this);
37923     },
37924
37925     // private
37926     shouldDeactivate : function(e){
37927         return !this.region || !this.region.contains(e.getPoint());
37928     },
37929
37930     // private
37931     handleClick : function(e){
37932         if(this.hideOnClick){
37933             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37934         }
37935     },
37936
37937     // private
37938     expandMenu : function(autoActivate){
37939         // do nothing
37940     },
37941
37942     // private
37943     hideMenu : function(){
37944         // do nothing
37945     }
37946 });/*
37947  * Based on:
37948  * Ext JS Library 1.1.1
37949  * Copyright(c) 2006-2007, Ext JS, LLC.
37950  *
37951  * Originally Released Under LGPL - original licence link has changed is not relivant.
37952  *
37953  * Fork - LGPL
37954  * <script type="text/javascript">
37955  */
37956  
37957 /**
37958  * @class Roo.menu.Adapter
37959  * @extends Roo.menu.BaseItem
37960  * 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.
37961  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37962  * @constructor
37963  * Creates a new Adapter
37964  * @param {Object} config Configuration options
37965  */
37966 Roo.menu.Adapter = function(component, config){
37967     Roo.menu.Adapter.superclass.constructor.call(this, config);
37968     this.component = component;
37969 };
37970 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37971     // private
37972     canActivate : true,
37973
37974     // private
37975     onRender : function(container, position){
37976         this.component.render(container);
37977         this.el = this.component.getEl();
37978     },
37979
37980     // private
37981     activate : function(){
37982         if(this.disabled){
37983             return false;
37984         }
37985         this.component.focus();
37986         this.fireEvent("activate", this);
37987         return true;
37988     },
37989
37990     // private
37991     deactivate : function(){
37992         this.fireEvent("deactivate", this);
37993     },
37994
37995     // private
37996     disable : function(){
37997         this.component.disable();
37998         Roo.menu.Adapter.superclass.disable.call(this);
37999     },
38000
38001     // private
38002     enable : function(){
38003         this.component.enable();
38004         Roo.menu.Adapter.superclass.enable.call(this);
38005     }
38006 });/*
38007  * Based on:
38008  * Ext JS Library 1.1.1
38009  * Copyright(c) 2006-2007, Ext JS, LLC.
38010  *
38011  * Originally Released Under LGPL - original licence link has changed is not relivant.
38012  *
38013  * Fork - LGPL
38014  * <script type="text/javascript">
38015  */
38016
38017 /**
38018  * @class Roo.menu.TextItem
38019  * @extends Roo.menu.BaseItem
38020  * Adds a static text string to a menu, usually used as either a heading or group separator.
38021  * Note: old style constructor with text is still supported.
38022  * 
38023  * @constructor
38024  * Creates a new TextItem
38025  * @param {Object} cfg Configuration
38026  */
38027 Roo.menu.TextItem = function(cfg){
38028     if (typeof(cfg) == 'string') {
38029         this.text = cfg;
38030     } else {
38031         Roo.apply(this,cfg);
38032     }
38033     
38034     Roo.menu.TextItem.superclass.constructor.call(this);
38035 };
38036
38037 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38038     /**
38039      * @cfg {Boolean} text Text to show on item.
38040      */
38041     text : '',
38042     
38043     /**
38044      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38045      */
38046     hideOnClick : false,
38047     /**
38048      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38049      */
38050     itemCls : "x-menu-text",
38051
38052     // private
38053     onRender : function(){
38054         var s = document.createElement("span");
38055         s.className = this.itemCls;
38056         s.innerHTML = this.text;
38057         this.el = s;
38058         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38059     }
38060 });/*
38061  * Based on:
38062  * Ext JS Library 1.1.1
38063  * Copyright(c) 2006-2007, Ext JS, LLC.
38064  *
38065  * Originally Released Under LGPL - original licence link has changed is not relivant.
38066  *
38067  * Fork - LGPL
38068  * <script type="text/javascript">
38069  */
38070
38071 /**
38072  * @class Roo.menu.Separator
38073  * @extends Roo.menu.BaseItem
38074  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38075  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38076  * @constructor
38077  * @param {Object} config Configuration options
38078  */
38079 Roo.menu.Separator = function(config){
38080     Roo.menu.Separator.superclass.constructor.call(this, config);
38081 };
38082
38083 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38084     /**
38085      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38086      */
38087     itemCls : "x-menu-sep",
38088     /**
38089      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38090      */
38091     hideOnClick : false,
38092
38093     // private
38094     onRender : function(li){
38095         var s = document.createElement("span");
38096         s.className = this.itemCls;
38097         s.innerHTML = "&#160;";
38098         this.el = s;
38099         li.addClass("x-menu-sep-li");
38100         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38101     }
38102 });/*
38103  * Based on:
38104  * Ext JS Library 1.1.1
38105  * Copyright(c) 2006-2007, Ext JS, LLC.
38106  *
38107  * Originally Released Under LGPL - original licence link has changed is not relivant.
38108  *
38109  * Fork - LGPL
38110  * <script type="text/javascript">
38111  */
38112 /**
38113  * @class Roo.menu.Item
38114  * @extends Roo.menu.BaseItem
38115  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38116  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38117  * activation and click handling.
38118  * @constructor
38119  * Creates a new Item
38120  * @param {Object} config Configuration options
38121  */
38122 Roo.menu.Item = function(config){
38123     Roo.menu.Item.superclass.constructor.call(this, config);
38124     if(this.menu){
38125         this.menu = Roo.menu.MenuMgr.get(this.menu);
38126     }
38127 };
38128 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38129     
38130     /**
38131      * @cfg {String} text
38132      * The text to show on the menu item.
38133      */
38134     text: '',
38135      /**
38136      * @cfg {String} HTML to render in menu
38137      * The text to show on the menu item (HTML version).
38138      */
38139     html: '',
38140     /**
38141      * @cfg {String} icon
38142      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38143      */
38144     icon: undefined,
38145     /**
38146      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38147      */
38148     itemCls : "x-menu-item",
38149     /**
38150      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38151      */
38152     canActivate : true,
38153     /**
38154      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38155      */
38156     showDelay: 200,
38157     // doc'd in BaseItem
38158     hideDelay: 200,
38159
38160     // private
38161     ctype: "Roo.menu.Item",
38162     
38163     // private
38164     onRender : function(container, position){
38165         var el = document.createElement("a");
38166         el.hideFocus = true;
38167         el.unselectable = "on";
38168         el.href = this.href || "#";
38169         if(this.hrefTarget){
38170             el.target = this.hrefTarget;
38171         }
38172         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38173         
38174         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38175         
38176         el.innerHTML = String.format(
38177                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38178                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38179         this.el = el;
38180         Roo.menu.Item.superclass.onRender.call(this, container, position);
38181     },
38182
38183     /**
38184      * Sets the text to display in this menu item
38185      * @param {String} text The text to display
38186      * @param {Boolean} isHTML true to indicate text is pure html.
38187      */
38188     setText : function(text, isHTML){
38189         if (isHTML) {
38190             this.html = text;
38191         } else {
38192             this.text = text;
38193             this.html = '';
38194         }
38195         if(this.rendered){
38196             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38197      
38198             this.el.update(String.format(
38199                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38200                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38201             this.parentMenu.autoWidth();
38202         }
38203     },
38204
38205     // private
38206     handleClick : function(e){
38207         if(!this.href){ // if no link defined, stop the event automatically
38208             e.stopEvent();
38209         }
38210         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38211     },
38212
38213     // private
38214     activate : function(autoExpand){
38215         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38216             this.focus();
38217             if(autoExpand){
38218                 this.expandMenu();
38219             }
38220         }
38221         return true;
38222     },
38223
38224     // private
38225     shouldDeactivate : function(e){
38226         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38227             if(this.menu && this.menu.isVisible()){
38228                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38229             }
38230             return true;
38231         }
38232         return false;
38233     },
38234
38235     // private
38236     deactivate : function(){
38237         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38238         this.hideMenu();
38239     },
38240
38241     // private
38242     expandMenu : function(autoActivate){
38243         if(!this.disabled && this.menu){
38244             clearTimeout(this.hideTimer);
38245             delete this.hideTimer;
38246             if(!this.menu.isVisible() && !this.showTimer){
38247                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38248             }else if (this.menu.isVisible() && autoActivate){
38249                 this.menu.tryActivate(0, 1);
38250             }
38251         }
38252     },
38253
38254     // private
38255     deferExpand : function(autoActivate){
38256         delete this.showTimer;
38257         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38258         if(autoActivate){
38259             this.menu.tryActivate(0, 1);
38260         }
38261     },
38262
38263     // private
38264     hideMenu : function(){
38265         clearTimeout(this.showTimer);
38266         delete this.showTimer;
38267         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38268             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38269         }
38270     },
38271
38272     // private
38273     deferHide : function(){
38274         delete this.hideTimer;
38275         this.menu.hide();
38276     }
38277 });/*
38278  * Based on:
38279  * Ext JS Library 1.1.1
38280  * Copyright(c) 2006-2007, Ext JS, LLC.
38281  *
38282  * Originally Released Under LGPL - original licence link has changed is not relivant.
38283  *
38284  * Fork - LGPL
38285  * <script type="text/javascript">
38286  */
38287  
38288 /**
38289  * @class Roo.menu.CheckItem
38290  * @extends Roo.menu.Item
38291  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38292  * @constructor
38293  * Creates a new CheckItem
38294  * @param {Object} config Configuration options
38295  */
38296 Roo.menu.CheckItem = function(config){
38297     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38298     this.addEvents({
38299         /**
38300          * @event beforecheckchange
38301          * Fires before the checked value is set, providing an opportunity to cancel if needed
38302          * @param {Roo.menu.CheckItem} this
38303          * @param {Boolean} checked The new checked value that will be set
38304          */
38305         "beforecheckchange" : true,
38306         /**
38307          * @event checkchange
38308          * Fires after the checked value has been set
38309          * @param {Roo.menu.CheckItem} this
38310          * @param {Boolean} checked The checked value that was set
38311          */
38312         "checkchange" : true
38313     });
38314     if(this.checkHandler){
38315         this.on('checkchange', this.checkHandler, this.scope);
38316     }
38317 };
38318 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38319     /**
38320      * @cfg {String} group
38321      * All check items with the same group name will automatically be grouped into a single-select
38322      * radio button group (defaults to '')
38323      */
38324     /**
38325      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38326      */
38327     itemCls : "x-menu-item x-menu-check-item",
38328     /**
38329      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38330      */
38331     groupClass : "x-menu-group-item",
38332
38333     /**
38334      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38335      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38336      * initialized with checked = true will be rendered as checked.
38337      */
38338     checked: false,
38339
38340     // private
38341     ctype: "Roo.menu.CheckItem",
38342
38343     // private
38344     onRender : function(c){
38345         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38346         if(this.group){
38347             this.el.addClass(this.groupClass);
38348         }
38349         Roo.menu.MenuMgr.registerCheckable(this);
38350         if(this.checked){
38351             this.checked = false;
38352             this.setChecked(true, true);
38353         }
38354     },
38355
38356     // private
38357     destroy : function(){
38358         if(this.rendered){
38359             Roo.menu.MenuMgr.unregisterCheckable(this);
38360         }
38361         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38362     },
38363
38364     /**
38365      * Set the checked state of this item
38366      * @param {Boolean} checked The new checked value
38367      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38368      */
38369     setChecked : function(state, suppressEvent){
38370         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38371             if(this.container){
38372                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38373             }
38374             this.checked = state;
38375             if(suppressEvent !== true){
38376                 this.fireEvent("checkchange", this, state);
38377             }
38378         }
38379     },
38380
38381     // private
38382     handleClick : function(e){
38383        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38384            this.setChecked(!this.checked);
38385        }
38386        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38387     }
38388 });/*
38389  * Based on:
38390  * Ext JS Library 1.1.1
38391  * Copyright(c) 2006-2007, Ext JS, LLC.
38392  *
38393  * Originally Released Under LGPL - original licence link has changed is not relivant.
38394  *
38395  * Fork - LGPL
38396  * <script type="text/javascript">
38397  */
38398  
38399 /**
38400  * @class Roo.menu.DateItem
38401  * @extends Roo.menu.Adapter
38402  * A menu item that wraps the {@link Roo.DatPicker} component.
38403  * @constructor
38404  * Creates a new DateItem
38405  * @param {Object} config Configuration options
38406  */
38407 Roo.menu.DateItem = function(config){
38408     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38409     /** The Roo.DatePicker object @type Roo.DatePicker */
38410     this.picker = this.component;
38411     this.addEvents({select: true});
38412     
38413     this.picker.on("render", function(picker){
38414         picker.getEl().swallowEvent("click");
38415         picker.container.addClass("x-menu-date-item");
38416     });
38417
38418     this.picker.on("select", this.onSelect, this);
38419 };
38420
38421 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38422     // private
38423     onSelect : function(picker, date){
38424         this.fireEvent("select", this, date, picker);
38425         Roo.menu.DateItem.superclass.handleClick.call(this);
38426     }
38427 });/*
38428  * Based on:
38429  * Ext JS Library 1.1.1
38430  * Copyright(c) 2006-2007, Ext JS, LLC.
38431  *
38432  * Originally Released Under LGPL - original licence link has changed is not relivant.
38433  *
38434  * Fork - LGPL
38435  * <script type="text/javascript">
38436  */
38437  
38438 /**
38439  * @class Roo.menu.ColorItem
38440  * @extends Roo.menu.Adapter
38441  * A menu item that wraps the {@link Roo.ColorPalette} component.
38442  * @constructor
38443  * Creates a new ColorItem
38444  * @param {Object} config Configuration options
38445  */
38446 Roo.menu.ColorItem = function(config){
38447     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38448     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38449     this.palette = this.component;
38450     this.relayEvents(this.palette, ["select"]);
38451     if(this.selectHandler){
38452         this.on('select', this.selectHandler, this.scope);
38453     }
38454 };
38455 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38456  * Based on:
38457  * Ext JS Library 1.1.1
38458  * Copyright(c) 2006-2007, Ext JS, LLC.
38459  *
38460  * Originally Released Under LGPL - original licence link has changed is not relivant.
38461  *
38462  * Fork - LGPL
38463  * <script type="text/javascript">
38464  */
38465  
38466
38467 /**
38468  * @class Roo.menu.DateMenu
38469  * @extends Roo.menu.Menu
38470  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38471  * @constructor
38472  * Creates a new DateMenu
38473  * @param {Object} config Configuration options
38474  */
38475 Roo.menu.DateMenu = function(config){
38476     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38477     this.plain = true;
38478     var di = new Roo.menu.DateItem(config);
38479     this.add(di);
38480     /**
38481      * The {@link Roo.DatePicker} instance for this DateMenu
38482      * @type DatePicker
38483      */
38484     this.picker = di.picker;
38485     /**
38486      * @event select
38487      * @param {DatePicker} picker
38488      * @param {Date} date
38489      */
38490     this.relayEvents(di, ["select"]);
38491     this.on('beforeshow', function(){
38492         if(this.picker){
38493             this.picker.hideMonthPicker(false);
38494         }
38495     }, this);
38496 };
38497 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38498     cls:'x-date-menu'
38499 });/*
38500  * Based on:
38501  * Ext JS Library 1.1.1
38502  * Copyright(c) 2006-2007, Ext JS, LLC.
38503  *
38504  * Originally Released Under LGPL - original licence link has changed is not relivant.
38505  *
38506  * Fork - LGPL
38507  * <script type="text/javascript">
38508  */
38509  
38510
38511 /**
38512  * @class Roo.menu.ColorMenu
38513  * @extends Roo.menu.Menu
38514  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38515  * @constructor
38516  * Creates a new ColorMenu
38517  * @param {Object} config Configuration options
38518  */
38519 Roo.menu.ColorMenu = function(config){
38520     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38521     this.plain = true;
38522     var ci = new Roo.menu.ColorItem(config);
38523     this.add(ci);
38524     /**
38525      * The {@link Roo.ColorPalette} instance for this ColorMenu
38526      * @type ColorPalette
38527      */
38528     this.palette = ci.palette;
38529     /**
38530      * @event select
38531      * @param {ColorPalette} palette
38532      * @param {String} color
38533      */
38534     this.relayEvents(ci, ["select"]);
38535 };
38536 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38537  * Based on:
38538  * Ext JS Library 1.1.1
38539  * Copyright(c) 2006-2007, Ext JS, LLC.
38540  *
38541  * Originally Released Under LGPL - original licence link has changed is not relivant.
38542  *
38543  * Fork - LGPL
38544  * <script type="text/javascript">
38545  */
38546  
38547 /**
38548  * @class Roo.form.Field
38549  * @extends Roo.BoxComponent
38550  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38551  * @constructor
38552  * Creates a new Field
38553  * @param {Object} config Configuration options
38554  */
38555 Roo.form.Field = function(config){
38556     Roo.form.Field.superclass.constructor.call(this, config);
38557 };
38558
38559 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38560     /**
38561      * @cfg {String} fieldLabel Label to use when rendering a form.
38562      */
38563        /**
38564      * @cfg {String} qtip Mouse over tip
38565      */
38566      
38567     /**
38568      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38569      */
38570     invalidClass : "x-form-invalid",
38571     /**
38572      * @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")
38573      */
38574     invalidText : "The value in this field is invalid",
38575     /**
38576      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38577      */
38578     focusClass : "x-form-focus",
38579     /**
38580      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38581       automatic validation (defaults to "keyup").
38582      */
38583     validationEvent : "keyup",
38584     /**
38585      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38586      */
38587     validateOnBlur : true,
38588     /**
38589      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38590      */
38591     validationDelay : 250,
38592     /**
38593      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38594      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38595      */
38596     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38597     /**
38598      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38599      */
38600     fieldClass : "x-form-field",
38601     /**
38602      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38603      *<pre>
38604 Value         Description
38605 -----------   ----------------------------------------------------------------------
38606 qtip          Display a quick tip when the user hovers over the field
38607 title         Display a default browser title attribute popup
38608 under         Add a block div beneath the field containing the error text
38609 side          Add an error icon to the right of the field with a popup on hover
38610 [element id]  Add the error text directly to the innerHTML of the specified element
38611 </pre>
38612      */
38613     msgTarget : 'qtip',
38614     /**
38615      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38616      */
38617     msgFx : 'normal',
38618
38619     /**
38620      * @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.
38621      */
38622     readOnly : false,
38623
38624     /**
38625      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38626      */
38627     disabled : false,
38628
38629     /**
38630      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38631      */
38632     inputType : undefined,
38633     
38634     /**
38635      * @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).
38636          */
38637         tabIndex : undefined,
38638         
38639     // private
38640     isFormField : true,
38641
38642     // private
38643     hasFocus : false,
38644     /**
38645      * @property {Roo.Element} fieldEl
38646      * Element Containing the rendered Field (with label etc.)
38647      */
38648     /**
38649      * @cfg {Mixed} value A value to initialize this field with.
38650      */
38651     value : undefined,
38652
38653     /**
38654      * @cfg {String} name The field's HTML name attribute.
38655      */
38656     /**
38657      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38658      */
38659     // private
38660     loadedValue : false,
38661      
38662      
38663         // private ??
38664         initComponent : function(){
38665         Roo.form.Field.superclass.initComponent.call(this);
38666         this.addEvents({
38667             /**
38668              * @event focus
38669              * Fires when this field receives input focus.
38670              * @param {Roo.form.Field} this
38671              */
38672             focus : true,
38673             /**
38674              * @event blur
38675              * Fires when this field loses input focus.
38676              * @param {Roo.form.Field} this
38677              */
38678             blur : true,
38679             /**
38680              * @event specialkey
38681              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38682              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38683              * @param {Roo.form.Field} this
38684              * @param {Roo.EventObject} e The event object
38685              */
38686             specialkey : true,
38687             /**
38688              * @event change
38689              * Fires just before the field blurs if the field value has changed.
38690              * @param {Roo.form.Field} this
38691              * @param {Mixed} newValue The new value
38692              * @param {Mixed} oldValue The original value
38693              */
38694             change : true,
38695             /**
38696              * @event invalid
38697              * Fires after the field has been marked as invalid.
38698              * @param {Roo.form.Field} this
38699              * @param {String} msg The validation message
38700              */
38701             invalid : true,
38702             /**
38703              * @event valid
38704              * Fires after the field has been validated with no errors.
38705              * @param {Roo.form.Field} this
38706              */
38707             valid : true,
38708              /**
38709              * @event keyup
38710              * Fires after the key up
38711              * @param {Roo.form.Field} this
38712              * @param {Roo.EventObject}  e The event Object
38713              */
38714             keyup : true
38715         });
38716     },
38717
38718     /**
38719      * Returns the name attribute of the field if available
38720      * @return {String} name The field name
38721      */
38722     getName: function(){
38723          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38724     },
38725
38726     // private
38727     onRender : function(ct, position){
38728         Roo.form.Field.superclass.onRender.call(this, ct, position);
38729         if(!this.el){
38730             var cfg = this.getAutoCreate();
38731             if(!cfg.name){
38732                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38733             }
38734             if (!cfg.name.length) {
38735                 delete cfg.name;
38736             }
38737             if(this.inputType){
38738                 cfg.type = this.inputType;
38739             }
38740             this.el = ct.createChild(cfg, position);
38741         }
38742         var type = this.el.dom.type;
38743         if(type){
38744             if(type == 'password'){
38745                 type = 'text';
38746             }
38747             this.el.addClass('x-form-'+type);
38748         }
38749         if(this.readOnly){
38750             this.el.dom.readOnly = true;
38751         }
38752         if(this.tabIndex !== undefined){
38753             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38754         }
38755
38756         this.el.addClass([this.fieldClass, this.cls]);
38757         this.initValue();
38758     },
38759
38760     /**
38761      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38762      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38763      * @return {Roo.form.Field} this
38764      */
38765     applyTo : function(target){
38766         this.allowDomMove = false;
38767         this.el = Roo.get(target);
38768         this.render(this.el.dom.parentNode);
38769         return this;
38770     },
38771
38772     // private
38773     initValue : function(){
38774         if(this.value !== undefined){
38775             this.setValue(this.value);
38776         }else if(this.el.dom.value.length > 0){
38777             this.setValue(this.el.dom.value);
38778         }
38779     },
38780
38781     /**
38782      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38783      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38784      */
38785     isDirty : function() {
38786         if(this.disabled) {
38787             return false;
38788         }
38789         return String(this.getValue()) !== String(this.originalValue);
38790     },
38791
38792     /**
38793      * stores the current value in loadedValue
38794      */
38795     resetHasChanged : function()
38796     {
38797         this.loadedValue = String(this.getValue());
38798     },
38799     /**
38800      * checks the current value against the 'loaded' value.
38801      * Note - will return false if 'resetHasChanged' has not been called first.
38802      */
38803     hasChanged : function()
38804     {
38805         if(this.disabled || this.readOnly) {
38806             return false;
38807         }
38808         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38809     },
38810     
38811     
38812     
38813     // private
38814     afterRender : function(){
38815         Roo.form.Field.superclass.afterRender.call(this);
38816         this.initEvents();
38817     },
38818
38819     // private
38820     fireKey : function(e){
38821         //Roo.log('field ' + e.getKey());
38822         if(e.isNavKeyPress()){
38823             this.fireEvent("specialkey", this, e);
38824         }
38825     },
38826
38827     /**
38828      * Resets the current field value to the originally loaded value and clears any validation messages
38829      */
38830     reset : function(){
38831         this.setValue(this.resetValue);
38832         this.clearInvalid();
38833     },
38834
38835     // private
38836     initEvents : function(){
38837         // safari killled keypress - so keydown is now used..
38838         this.el.on("keydown" , this.fireKey,  this);
38839         this.el.on("focus", this.onFocus,  this);
38840         this.el.on("blur", this.onBlur,  this);
38841         this.el.relayEvent('keyup', this);
38842
38843         // reference to original value for reset
38844         this.originalValue = this.getValue();
38845         this.resetValue =  this.getValue();
38846     },
38847
38848     // private
38849     onFocus : function(){
38850         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38851             this.el.addClass(this.focusClass);
38852         }
38853         if(!this.hasFocus){
38854             this.hasFocus = true;
38855             this.startValue = this.getValue();
38856             this.fireEvent("focus", this);
38857         }
38858     },
38859
38860     beforeBlur : Roo.emptyFn,
38861
38862     // private
38863     onBlur : function(){
38864         this.beforeBlur();
38865         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38866             this.el.removeClass(this.focusClass);
38867         }
38868         this.hasFocus = false;
38869         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38870             this.validate();
38871         }
38872         var v = this.getValue();
38873         if(String(v) !== String(this.startValue)){
38874             this.fireEvent('change', this, v, this.startValue);
38875         }
38876         this.fireEvent("blur", this);
38877     },
38878
38879     /**
38880      * Returns whether or not the field value is currently valid
38881      * @param {Boolean} preventMark True to disable marking the field invalid
38882      * @return {Boolean} True if the value is valid, else false
38883      */
38884     isValid : function(preventMark){
38885         if(this.disabled){
38886             return true;
38887         }
38888         var restore = this.preventMark;
38889         this.preventMark = preventMark === true;
38890         var v = this.validateValue(this.processValue(this.getRawValue()));
38891         this.preventMark = restore;
38892         return v;
38893     },
38894
38895     /**
38896      * Validates the field value
38897      * @return {Boolean} True if the value is valid, else false
38898      */
38899     validate : function(){
38900         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38901             this.clearInvalid();
38902             return true;
38903         }
38904         return false;
38905     },
38906
38907     processValue : function(value){
38908         return value;
38909     },
38910
38911     // private
38912     // Subclasses should provide the validation implementation by overriding this
38913     validateValue : function(value){
38914         return true;
38915     },
38916
38917     /**
38918      * Mark this field as invalid
38919      * @param {String} msg The validation message
38920      */
38921     markInvalid : function(msg){
38922         if(!this.rendered || this.preventMark){ // not rendered
38923             return;
38924         }
38925         
38926         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38927         
38928         obj.el.addClass(this.invalidClass);
38929         msg = msg || this.invalidText;
38930         switch(this.msgTarget){
38931             case 'qtip':
38932                 obj.el.dom.qtip = msg;
38933                 obj.el.dom.qclass = 'x-form-invalid-tip';
38934                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38935                     Roo.QuickTips.enable();
38936                 }
38937                 break;
38938             case 'title':
38939                 this.el.dom.title = msg;
38940                 break;
38941             case 'under':
38942                 if(!this.errorEl){
38943                     var elp = this.el.findParent('.x-form-element', 5, true);
38944                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38945                     this.errorEl.setWidth(elp.getWidth(true)-20);
38946                 }
38947                 this.errorEl.update(msg);
38948                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38949                 break;
38950             case 'side':
38951                 if(!this.errorIcon){
38952                     var elp = this.el.findParent('.x-form-element', 5, true);
38953                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38954                 }
38955                 this.alignErrorIcon();
38956                 this.errorIcon.dom.qtip = msg;
38957                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38958                 this.errorIcon.show();
38959                 this.on('resize', this.alignErrorIcon, this);
38960                 break;
38961             default:
38962                 var t = Roo.getDom(this.msgTarget);
38963                 t.innerHTML = msg;
38964                 t.style.display = this.msgDisplay;
38965                 break;
38966         }
38967         this.fireEvent('invalid', this, msg);
38968     },
38969
38970     // private
38971     alignErrorIcon : function(){
38972         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38973     },
38974
38975     /**
38976      * Clear any invalid styles/messages for this field
38977      */
38978     clearInvalid : function(){
38979         if(!this.rendered || this.preventMark){ // not rendered
38980             return;
38981         }
38982         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38983         
38984         obj.el.removeClass(this.invalidClass);
38985         switch(this.msgTarget){
38986             case 'qtip':
38987                 obj.el.dom.qtip = '';
38988                 break;
38989             case 'title':
38990                 this.el.dom.title = '';
38991                 break;
38992             case 'under':
38993                 if(this.errorEl){
38994                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38995                 }
38996                 break;
38997             case 'side':
38998                 if(this.errorIcon){
38999                     this.errorIcon.dom.qtip = '';
39000                     this.errorIcon.hide();
39001                     this.un('resize', this.alignErrorIcon, this);
39002                 }
39003                 break;
39004             default:
39005                 var t = Roo.getDom(this.msgTarget);
39006                 t.innerHTML = '';
39007                 t.style.display = 'none';
39008                 break;
39009         }
39010         this.fireEvent('valid', this);
39011     },
39012
39013     /**
39014      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39015      * @return {Mixed} value The field value
39016      */
39017     getRawValue : function(){
39018         var v = this.el.getValue();
39019         
39020         return v;
39021     },
39022
39023     /**
39024      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39025      * @return {Mixed} value The field value
39026      */
39027     getValue : function(){
39028         var v = this.el.getValue();
39029          
39030         return v;
39031     },
39032
39033     /**
39034      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39035      * @param {Mixed} value The value to set
39036      */
39037     setRawValue : function(v){
39038         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39039     },
39040
39041     /**
39042      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39043      * @param {Mixed} value The value to set
39044      */
39045     setValue : function(v){
39046         this.value = v;
39047         if(this.rendered){
39048             this.el.dom.value = (v === null || v === undefined ? '' : v);
39049              this.validate();
39050         }
39051     },
39052
39053     adjustSize : function(w, h){
39054         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39055         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39056         return s;
39057     },
39058
39059     adjustWidth : function(tag, w){
39060         tag = tag.toLowerCase();
39061         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39062             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39063                 if(tag == 'input'){
39064                     return w + 2;
39065                 }
39066                 if(tag == 'textarea'){
39067                     return w-2;
39068                 }
39069             }else if(Roo.isOpera){
39070                 if(tag == 'input'){
39071                     return w + 2;
39072                 }
39073                 if(tag == 'textarea'){
39074                     return w-2;
39075                 }
39076             }
39077         }
39078         return w;
39079     }
39080 });
39081
39082
39083 // anything other than normal should be considered experimental
39084 Roo.form.Field.msgFx = {
39085     normal : {
39086         show: function(msgEl, f){
39087             msgEl.setDisplayed('block');
39088         },
39089
39090         hide : function(msgEl, f){
39091             msgEl.setDisplayed(false).update('');
39092         }
39093     },
39094
39095     slide : {
39096         show: function(msgEl, f){
39097             msgEl.slideIn('t', {stopFx:true});
39098         },
39099
39100         hide : function(msgEl, f){
39101             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39102         }
39103     },
39104
39105     slideRight : {
39106         show: function(msgEl, f){
39107             msgEl.fixDisplay();
39108             msgEl.alignTo(f.el, 'tl-tr');
39109             msgEl.slideIn('l', {stopFx:true});
39110         },
39111
39112         hide : function(msgEl, f){
39113             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39114         }
39115     }
39116 };/*
39117  * Based on:
39118  * Ext JS Library 1.1.1
39119  * Copyright(c) 2006-2007, Ext JS, LLC.
39120  *
39121  * Originally Released Under LGPL - original licence link has changed is not relivant.
39122  *
39123  * Fork - LGPL
39124  * <script type="text/javascript">
39125  */
39126  
39127
39128 /**
39129  * @class Roo.form.TextField
39130  * @extends Roo.form.Field
39131  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39132  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39133  * @constructor
39134  * Creates a new TextField
39135  * @param {Object} config Configuration options
39136  */
39137 Roo.form.TextField = function(config){
39138     Roo.form.TextField.superclass.constructor.call(this, config);
39139     this.addEvents({
39140         /**
39141          * @event autosize
39142          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39143          * according to the default logic, but this event provides a hook for the developer to apply additional
39144          * logic at runtime to resize the field if needed.
39145              * @param {Roo.form.Field} this This text field
39146              * @param {Number} width The new field width
39147              */
39148         autosize : true
39149     });
39150 };
39151
39152 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39153     /**
39154      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39155      */
39156     grow : false,
39157     /**
39158      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39159      */
39160     growMin : 30,
39161     /**
39162      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39163      */
39164     growMax : 800,
39165     /**
39166      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39167      */
39168     vtype : null,
39169     /**
39170      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39171      */
39172     maskRe : null,
39173     /**
39174      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39175      */
39176     disableKeyFilter : false,
39177     /**
39178      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39179      */
39180     allowBlank : true,
39181     /**
39182      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39183      */
39184     minLength : 0,
39185     /**
39186      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39187      */
39188     maxLength : Number.MAX_VALUE,
39189     /**
39190      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39191      */
39192     minLengthText : "The minimum length for this field is {0}",
39193     /**
39194      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39195      */
39196     maxLengthText : "The maximum length for this field is {0}",
39197     /**
39198      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39199      */
39200     selectOnFocus : false,
39201     /**
39202      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39203      */
39204     blankText : "This field is required",
39205     /**
39206      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39207      * If available, this function will be called only after the basic validators all return true, and will be passed the
39208      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39209      */
39210     validator : null,
39211     /**
39212      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39213      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39214      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39215      */
39216     regex : null,
39217     /**
39218      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39219      */
39220     regexText : "",
39221     /**
39222      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39223      */
39224     emptyText : null,
39225    
39226
39227     // private
39228     initEvents : function()
39229     {
39230         if (this.emptyText) {
39231             this.el.attr('placeholder', this.emptyText);
39232         }
39233         
39234         Roo.form.TextField.superclass.initEvents.call(this);
39235         if(this.validationEvent == 'keyup'){
39236             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39237             this.el.on('keyup', this.filterValidation, this);
39238         }
39239         else if(this.validationEvent !== false){
39240             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39241         }
39242         
39243         if(this.selectOnFocus){
39244             this.on("focus", this.preFocus, this);
39245             
39246         }
39247         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39248             this.el.on("keypress", this.filterKeys, this);
39249         }
39250         if(this.grow){
39251             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39252             this.el.on("click", this.autoSize,  this);
39253         }
39254         if(this.el.is('input[type=password]') && Roo.isSafari){
39255             this.el.on('keydown', this.SafariOnKeyDown, this);
39256         }
39257     },
39258
39259     processValue : function(value){
39260         if(this.stripCharsRe){
39261             var newValue = value.replace(this.stripCharsRe, '');
39262             if(newValue !== value){
39263                 this.setRawValue(newValue);
39264                 return newValue;
39265             }
39266         }
39267         return value;
39268     },
39269
39270     filterValidation : function(e){
39271         if(!e.isNavKeyPress()){
39272             this.validationTask.delay(this.validationDelay);
39273         }
39274     },
39275
39276     // private
39277     onKeyUp : function(e){
39278         if(!e.isNavKeyPress()){
39279             this.autoSize();
39280         }
39281     },
39282
39283     /**
39284      * Resets the current field value to the originally-loaded value and clears any validation messages.
39285      *  
39286      */
39287     reset : function(){
39288         Roo.form.TextField.superclass.reset.call(this);
39289        
39290     },
39291
39292     
39293     // private
39294     preFocus : function(){
39295         
39296         if(this.selectOnFocus){
39297             this.el.dom.select();
39298         }
39299     },
39300
39301     
39302     // private
39303     filterKeys : function(e){
39304         var k = e.getKey();
39305         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39306             return;
39307         }
39308         var c = e.getCharCode(), cc = String.fromCharCode(c);
39309         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39310             return;
39311         }
39312         if(!this.maskRe.test(cc)){
39313             e.stopEvent();
39314         }
39315     },
39316
39317     setValue : function(v){
39318         
39319         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39320         
39321         this.autoSize();
39322     },
39323
39324     /**
39325      * Validates a value according to the field's validation rules and marks the field as invalid
39326      * if the validation fails
39327      * @param {Mixed} value The value to validate
39328      * @return {Boolean} True if the value is valid, else false
39329      */
39330     validateValue : function(value){
39331         if(value.length < 1)  { // if it's blank
39332              if(this.allowBlank){
39333                 this.clearInvalid();
39334                 return true;
39335              }else{
39336                 this.markInvalid(this.blankText);
39337                 return false;
39338              }
39339         }
39340         if(value.length < this.minLength){
39341             this.markInvalid(String.format(this.minLengthText, this.minLength));
39342             return false;
39343         }
39344         if(value.length > this.maxLength){
39345             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39346             return false;
39347         }
39348         if(this.vtype){
39349             var vt = Roo.form.VTypes;
39350             if(!vt[this.vtype](value, this)){
39351                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39352                 return false;
39353             }
39354         }
39355         if(typeof this.validator == "function"){
39356             var msg = this.validator(value);
39357             if(msg !== true){
39358                 this.markInvalid(msg);
39359                 return false;
39360             }
39361         }
39362         if(this.regex && !this.regex.test(value)){
39363             this.markInvalid(this.regexText);
39364             return false;
39365         }
39366         return true;
39367     },
39368
39369     /**
39370      * Selects text in this field
39371      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39372      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39373      */
39374     selectText : function(start, end){
39375         var v = this.getRawValue();
39376         if(v.length > 0){
39377             start = start === undefined ? 0 : start;
39378             end = end === undefined ? v.length : end;
39379             var d = this.el.dom;
39380             if(d.setSelectionRange){
39381                 d.setSelectionRange(start, end);
39382             }else if(d.createTextRange){
39383                 var range = d.createTextRange();
39384                 range.moveStart("character", start);
39385                 range.moveEnd("character", v.length-end);
39386                 range.select();
39387             }
39388         }
39389     },
39390
39391     /**
39392      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39393      * This only takes effect if grow = true, and fires the autosize event.
39394      */
39395     autoSize : function(){
39396         if(!this.grow || !this.rendered){
39397             return;
39398         }
39399         if(!this.metrics){
39400             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39401         }
39402         var el = this.el;
39403         var v = el.dom.value;
39404         var d = document.createElement('div');
39405         d.appendChild(document.createTextNode(v));
39406         v = d.innerHTML;
39407         d = null;
39408         v += "&#160;";
39409         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39410         this.el.setWidth(w);
39411         this.fireEvent("autosize", this, w);
39412     },
39413     
39414     // private
39415     SafariOnKeyDown : function(event)
39416     {
39417         // this is a workaround for a password hang bug on chrome/ webkit.
39418         
39419         var isSelectAll = false;
39420         
39421         if(this.el.dom.selectionEnd > 0){
39422             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39423         }
39424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39425             event.preventDefault();
39426             this.setValue('');
39427             return;
39428         }
39429         
39430         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39431             
39432             event.preventDefault();
39433             // this is very hacky as keydown always get's upper case.
39434             
39435             var cc = String.fromCharCode(event.getCharCode());
39436             
39437             
39438             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39439             
39440         }
39441         
39442         
39443     }
39444 });/*
39445  * Based on:
39446  * Ext JS Library 1.1.1
39447  * Copyright(c) 2006-2007, Ext JS, LLC.
39448  *
39449  * Originally Released Under LGPL - original licence link has changed is not relivant.
39450  *
39451  * Fork - LGPL
39452  * <script type="text/javascript">
39453  */
39454  
39455 /**
39456  * @class Roo.form.Hidden
39457  * @extends Roo.form.TextField
39458  * Simple Hidden element used on forms 
39459  * 
39460  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39461  * 
39462  * @constructor
39463  * Creates a new Hidden form element.
39464  * @param {Object} config Configuration options
39465  */
39466
39467
39468
39469 // easy hidden field...
39470 Roo.form.Hidden = function(config){
39471     Roo.form.Hidden.superclass.constructor.call(this, config);
39472 };
39473   
39474 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39475     fieldLabel:      '',
39476     inputType:      'hidden',
39477     width:          50,
39478     allowBlank:     true,
39479     labelSeparator: '',
39480     hidden:         true,
39481     itemCls :       'x-form-item-display-none'
39482
39483
39484 });
39485
39486
39487 /*
39488  * Based on:
39489  * Ext JS Library 1.1.1
39490  * Copyright(c) 2006-2007, Ext JS, LLC.
39491  *
39492  * Originally Released Under LGPL - original licence link has changed is not relivant.
39493  *
39494  * Fork - LGPL
39495  * <script type="text/javascript">
39496  */
39497  
39498 /**
39499  * @class Roo.form.TriggerField
39500  * @extends Roo.form.TextField
39501  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39502  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39503  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39504  * for which you can provide a custom implementation.  For example:
39505  * <pre><code>
39506 var trigger = new Roo.form.TriggerField();
39507 trigger.onTriggerClick = myTriggerFn;
39508 trigger.applyTo('my-field');
39509 </code></pre>
39510  *
39511  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39512  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39513  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39514  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39515  * @constructor
39516  * Create a new TriggerField.
39517  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39518  * to the base TextField)
39519  */
39520 Roo.form.TriggerField = function(config){
39521     this.mimicing = false;
39522     Roo.form.TriggerField.superclass.constructor.call(this, config);
39523 };
39524
39525 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39526     /**
39527      * @cfg {String} triggerClass A CSS class to apply to the trigger
39528      */
39529     /**
39530      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39531      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39532      */
39533     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39534     /**
39535      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39536      */
39537     hideTrigger:false,
39538
39539     /** @cfg {Boolean} grow @hide */
39540     /** @cfg {Number} growMin @hide */
39541     /** @cfg {Number} growMax @hide */
39542
39543     /**
39544      * @hide 
39545      * @method
39546      */
39547     autoSize: Roo.emptyFn,
39548     // private
39549     monitorTab : true,
39550     // private
39551     deferHeight : true,
39552
39553     
39554     actionMode : 'wrap',
39555     // private
39556     onResize : function(w, h){
39557         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39558         if(typeof w == 'number'){
39559             var x = w - this.trigger.getWidth();
39560             this.el.setWidth(this.adjustWidth('input', x));
39561             this.trigger.setStyle('left', x+'px');
39562         }
39563     },
39564
39565     // private
39566     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39567
39568     // private
39569     getResizeEl : function(){
39570         return this.wrap;
39571     },
39572
39573     // private
39574     getPositionEl : function(){
39575         return this.wrap;
39576     },
39577
39578     // private
39579     alignErrorIcon : function(){
39580         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39581     },
39582
39583     // private
39584     onRender : function(ct, position){
39585         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39586         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39587         this.trigger = this.wrap.createChild(this.triggerConfig ||
39588                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39589         if(this.hideTrigger){
39590             this.trigger.setDisplayed(false);
39591         }
39592         this.initTrigger();
39593         if(!this.width){
39594             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39595         }
39596     },
39597
39598     // private
39599     initTrigger : function(){
39600         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39601         this.trigger.addClassOnOver('x-form-trigger-over');
39602         this.trigger.addClassOnClick('x-form-trigger-click');
39603     },
39604
39605     // private
39606     onDestroy : function(){
39607         if(this.trigger){
39608             this.trigger.removeAllListeners();
39609             this.trigger.remove();
39610         }
39611         if(this.wrap){
39612             this.wrap.remove();
39613         }
39614         Roo.form.TriggerField.superclass.onDestroy.call(this);
39615     },
39616
39617     // private
39618     onFocus : function(){
39619         Roo.form.TriggerField.superclass.onFocus.call(this);
39620         if(!this.mimicing){
39621             this.wrap.addClass('x-trigger-wrap-focus');
39622             this.mimicing = true;
39623             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39624             if(this.monitorTab){
39625                 this.el.on("keydown", this.checkTab, this);
39626             }
39627         }
39628     },
39629
39630     // private
39631     checkTab : function(e){
39632         if(e.getKey() == e.TAB){
39633             this.triggerBlur();
39634         }
39635     },
39636
39637     // private
39638     onBlur : function(){
39639         // do nothing
39640     },
39641
39642     // private
39643     mimicBlur : function(e, t){
39644         if(!this.wrap.contains(t) && this.validateBlur()){
39645             this.triggerBlur();
39646         }
39647     },
39648
39649     // private
39650     triggerBlur : function(){
39651         this.mimicing = false;
39652         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39653         if(this.monitorTab){
39654             this.el.un("keydown", this.checkTab, this);
39655         }
39656         this.wrap.removeClass('x-trigger-wrap-focus');
39657         Roo.form.TriggerField.superclass.onBlur.call(this);
39658     },
39659
39660     // private
39661     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39662     validateBlur : function(e, t){
39663         return true;
39664     },
39665
39666     // private
39667     onDisable : function(){
39668         Roo.form.TriggerField.superclass.onDisable.call(this);
39669         if(this.wrap){
39670             this.wrap.addClass('x-item-disabled');
39671         }
39672     },
39673
39674     // private
39675     onEnable : function(){
39676         Roo.form.TriggerField.superclass.onEnable.call(this);
39677         if(this.wrap){
39678             this.wrap.removeClass('x-item-disabled');
39679         }
39680     },
39681
39682     // private
39683     onShow : function(){
39684         var ae = this.getActionEl();
39685         
39686         if(ae){
39687             ae.dom.style.display = '';
39688             ae.dom.style.visibility = 'visible';
39689         }
39690     },
39691
39692     // private
39693     
39694     onHide : function(){
39695         var ae = this.getActionEl();
39696         ae.dom.style.display = 'none';
39697     },
39698
39699     /**
39700      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39701      * by an implementing function.
39702      * @method
39703      * @param {EventObject} e
39704      */
39705     onTriggerClick : Roo.emptyFn
39706 });
39707
39708 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39709 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39710 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39711 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39712     initComponent : function(){
39713         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39714
39715         this.triggerConfig = {
39716             tag:'span', cls:'x-form-twin-triggers', cn:[
39717             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39718             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39719         ]};
39720     },
39721
39722     getTrigger : function(index){
39723         return this.triggers[index];
39724     },
39725
39726     initTrigger : function(){
39727         var ts = this.trigger.select('.x-form-trigger', true);
39728         this.wrap.setStyle('overflow', 'hidden');
39729         var triggerField = this;
39730         ts.each(function(t, all, index){
39731             t.hide = function(){
39732                 var w = triggerField.wrap.getWidth();
39733                 this.dom.style.display = 'none';
39734                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39735             };
39736             t.show = function(){
39737                 var w = triggerField.wrap.getWidth();
39738                 this.dom.style.display = '';
39739                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39740             };
39741             var triggerIndex = 'Trigger'+(index+1);
39742
39743             if(this['hide'+triggerIndex]){
39744                 t.dom.style.display = 'none';
39745             }
39746             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39747             t.addClassOnOver('x-form-trigger-over');
39748             t.addClassOnClick('x-form-trigger-click');
39749         }, this);
39750         this.triggers = ts.elements;
39751     },
39752
39753     onTrigger1Click : Roo.emptyFn,
39754     onTrigger2Click : Roo.emptyFn
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.form.TextArea
39768  * @extends Roo.form.TextField
39769  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39770  * support for auto-sizing.
39771  * @constructor
39772  * Creates a new TextArea
39773  * @param {Object} config Configuration options
39774  */
39775 Roo.form.TextArea = function(config){
39776     Roo.form.TextArea.superclass.constructor.call(this, config);
39777     // these are provided exchanges for backwards compat
39778     // minHeight/maxHeight were replaced by growMin/growMax to be
39779     // compatible with TextField growing config values
39780     if(this.minHeight !== undefined){
39781         this.growMin = this.minHeight;
39782     }
39783     if(this.maxHeight !== undefined){
39784         this.growMax = this.maxHeight;
39785     }
39786 };
39787
39788 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39789     /**
39790      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39791      */
39792     growMin : 60,
39793     /**
39794      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39795      */
39796     growMax: 1000,
39797     /**
39798      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39799      * in the field (equivalent to setting overflow: hidden, defaults to false)
39800      */
39801     preventScrollbars: false,
39802     /**
39803      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39804      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39805      */
39806
39807     // private
39808     onRender : function(ct, position){
39809         if(!this.el){
39810             this.defaultAutoCreate = {
39811                 tag: "textarea",
39812                 style:"width:300px;height:60px;",
39813                 autocomplete: "new-password"
39814             };
39815         }
39816         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39817         if(this.grow){
39818             this.textSizeEl = Roo.DomHelper.append(document.body, {
39819                 tag: "pre", cls: "x-form-grow-sizer"
39820             });
39821             if(this.preventScrollbars){
39822                 this.el.setStyle("overflow", "hidden");
39823             }
39824             this.el.setHeight(this.growMin);
39825         }
39826     },
39827
39828     onDestroy : function(){
39829         if(this.textSizeEl){
39830             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39831         }
39832         Roo.form.TextArea.superclass.onDestroy.call(this);
39833     },
39834
39835     // private
39836     onKeyUp : function(e){
39837         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39838             this.autoSize();
39839         }
39840     },
39841
39842     /**
39843      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39844      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39845      */
39846     autoSize : function(){
39847         if(!this.grow || !this.textSizeEl){
39848             return;
39849         }
39850         var el = this.el;
39851         var v = el.dom.value;
39852         var ts = this.textSizeEl;
39853
39854         ts.innerHTML = '';
39855         ts.appendChild(document.createTextNode(v));
39856         v = ts.innerHTML;
39857
39858         Roo.fly(ts).setWidth(this.el.getWidth());
39859         if(v.length < 1){
39860             v = "&#160;&#160;";
39861         }else{
39862             if(Roo.isIE){
39863                 v = v.replace(/\n/g, '<p>&#160;</p>');
39864             }
39865             v += "&#160;\n&#160;";
39866         }
39867         ts.innerHTML = v;
39868         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39869         if(h != this.lastHeight){
39870             this.lastHeight = h;
39871             this.el.setHeight(h);
39872             this.fireEvent("autosize", this, h);
39873         }
39874     }
39875 });/*
39876  * Based on:
39877  * Ext JS Library 1.1.1
39878  * Copyright(c) 2006-2007, Ext JS, LLC.
39879  *
39880  * Originally Released Under LGPL - original licence link has changed is not relivant.
39881  *
39882  * Fork - LGPL
39883  * <script type="text/javascript">
39884  */
39885  
39886
39887 /**
39888  * @class Roo.form.NumberField
39889  * @extends Roo.form.TextField
39890  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39891  * @constructor
39892  * Creates a new NumberField
39893  * @param {Object} config Configuration options
39894  */
39895 Roo.form.NumberField = function(config){
39896     Roo.form.NumberField.superclass.constructor.call(this, config);
39897 };
39898
39899 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39900     /**
39901      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39902      */
39903     fieldClass: "x-form-field x-form-num-field",
39904     /**
39905      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39906      */
39907     allowDecimals : true,
39908     /**
39909      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39910      */
39911     decimalSeparator : ".",
39912     /**
39913      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39914      */
39915     decimalPrecision : 2,
39916     /**
39917      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39918      */
39919     allowNegative : true,
39920     /**
39921      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39922      */
39923     minValue : Number.NEGATIVE_INFINITY,
39924     /**
39925      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39926      */
39927     maxValue : Number.MAX_VALUE,
39928     /**
39929      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39930      */
39931     minText : "The minimum value for this field is {0}",
39932     /**
39933      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39934      */
39935     maxText : "The maximum value for this field is {0}",
39936     /**
39937      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39938      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39939      */
39940     nanText : "{0} is not a valid number",
39941
39942     // private
39943     initEvents : function(){
39944         Roo.form.NumberField.superclass.initEvents.call(this);
39945         var allowed = "0123456789";
39946         if(this.allowDecimals){
39947             allowed += this.decimalSeparator;
39948         }
39949         if(this.allowNegative){
39950             allowed += "-";
39951         }
39952         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39953         var keyPress = function(e){
39954             var k = e.getKey();
39955             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39956                 return;
39957             }
39958             var c = e.getCharCode();
39959             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39960                 e.stopEvent();
39961             }
39962         };
39963         this.el.on("keypress", keyPress, this);
39964     },
39965
39966     // private
39967     validateValue : function(value){
39968         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39969             return false;
39970         }
39971         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39972              return true;
39973         }
39974         var num = this.parseValue(value);
39975         if(isNaN(num)){
39976             this.markInvalid(String.format(this.nanText, value));
39977             return false;
39978         }
39979         if(num < this.minValue){
39980             this.markInvalid(String.format(this.minText, this.minValue));
39981             return false;
39982         }
39983         if(num > this.maxValue){
39984             this.markInvalid(String.format(this.maxText, this.maxValue));
39985             return false;
39986         }
39987         return true;
39988     },
39989
39990     getValue : function(){
39991         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39992     },
39993
39994     // private
39995     parseValue : function(value){
39996         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39997         return isNaN(value) ? '' : value;
39998     },
39999
40000     // private
40001     fixPrecision : function(value){
40002         var nan = isNaN(value);
40003         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40004             return nan ? '' : value;
40005         }
40006         return parseFloat(value).toFixed(this.decimalPrecision);
40007     },
40008
40009     setValue : function(v){
40010         v = this.fixPrecision(v);
40011         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40012     },
40013
40014     // private
40015     decimalPrecisionFcn : function(v){
40016         return Math.floor(v);
40017     },
40018
40019     beforeBlur : function(){
40020         var v = this.parseValue(this.getRawValue());
40021         if(v){
40022             this.setValue(v);
40023         }
40024     }
40025 });/*
40026  * Based on:
40027  * Ext JS Library 1.1.1
40028  * Copyright(c) 2006-2007, Ext JS, LLC.
40029  *
40030  * Originally Released Under LGPL - original licence link has changed is not relivant.
40031  *
40032  * Fork - LGPL
40033  * <script type="text/javascript">
40034  */
40035  
40036 /**
40037  * @class Roo.form.DateField
40038  * @extends Roo.form.TriggerField
40039  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40040 * @constructor
40041 * Create a new DateField
40042 * @param {Object} config
40043  */
40044 Roo.form.DateField = function(config){
40045     Roo.form.DateField.superclass.constructor.call(this, config);
40046     
40047       this.addEvents({
40048          
40049         /**
40050          * @event select
40051          * Fires when a date is selected
40052              * @param {Roo.form.DateField} combo This combo box
40053              * @param {Date} date The date selected
40054              */
40055         'select' : true
40056          
40057     });
40058     
40059     
40060     if(typeof this.minValue == "string") {
40061         this.minValue = this.parseDate(this.minValue);
40062     }
40063     if(typeof this.maxValue == "string") {
40064         this.maxValue = this.parseDate(this.maxValue);
40065     }
40066     this.ddMatch = null;
40067     if(this.disabledDates){
40068         var dd = this.disabledDates;
40069         var re = "(?:";
40070         for(var i = 0; i < dd.length; i++){
40071             re += dd[i];
40072             if(i != dd.length-1) {
40073                 re += "|";
40074             }
40075         }
40076         this.ddMatch = new RegExp(re + ")");
40077     }
40078 };
40079
40080 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40081     /**
40082      * @cfg {String} format
40083      * The default date format string which can be overriden for localization support.  The format must be
40084      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40085      */
40086     format : "m/d/y",
40087     /**
40088      * @cfg {String} altFormats
40089      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40090      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40091      */
40092     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40093     /**
40094      * @cfg {Array} disabledDays
40095      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40096      */
40097     disabledDays : null,
40098     /**
40099      * @cfg {String} disabledDaysText
40100      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40101      */
40102     disabledDaysText : "Disabled",
40103     /**
40104      * @cfg {Array} disabledDates
40105      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40106      * expression so they are very powerful. Some examples:
40107      * <ul>
40108      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40109      * <li>["03/08", "09/16"] would disable those days for every year</li>
40110      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40111      * <li>["03/../2006"] would disable every day in March 2006</li>
40112      * <li>["^03"] would disable every day in every March</li>
40113      * </ul>
40114      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40115      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40116      */
40117     disabledDates : null,
40118     /**
40119      * @cfg {String} disabledDatesText
40120      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40121      */
40122     disabledDatesText : "Disabled",
40123     /**
40124      * @cfg {Date/String} minValue
40125      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40126      * valid format (defaults to null).
40127      */
40128     minValue : null,
40129     /**
40130      * @cfg {Date/String} maxValue
40131      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40132      * valid format (defaults to null).
40133      */
40134     maxValue : null,
40135     /**
40136      * @cfg {String} minText
40137      * The error text to display when the date in the cell is before minValue (defaults to
40138      * 'The date in this field must be after {minValue}').
40139      */
40140     minText : "The date in this field must be equal to or after {0}",
40141     /**
40142      * @cfg {String} maxText
40143      * The error text to display when the date in the cell is after maxValue (defaults to
40144      * 'The date in this field must be before {maxValue}').
40145      */
40146     maxText : "The date in this field must be equal to or before {0}",
40147     /**
40148      * @cfg {String} invalidText
40149      * The error text to display when the date in the field is invalid (defaults to
40150      * '{value} is not a valid date - it must be in the format {format}').
40151      */
40152     invalidText : "{0} is not a valid date - it must be in the format {1}",
40153     /**
40154      * @cfg {String} triggerClass
40155      * An additional CSS class used to style the trigger button.  The trigger will always get the
40156      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40157      * which displays a calendar icon).
40158      */
40159     triggerClass : 'x-form-date-trigger',
40160     
40161
40162     /**
40163      * @cfg {Boolean} useIso
40164      * if enabled, then the date field will use a hidden field to store the 
40165      * real value as iso formated date. default (false)
40166      */ 
40167     useIso : false,
40168     /**
40169      * @cfg {String/Object} autoCreate
40170      * A DomHelper element spec, or true for a default element spec (defaults to
40171      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40172      */ 
40173     // private
40174     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40175     
40176     // private
40177     hiddenField: false,
40178     
40179     onRender : function(ct, position)
40180     {
40181         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40182         if (this.useIso) {
40183             //this.el.dom.removeAttribute('name'); 
40184             Roo.log("Changing name?");
40185             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40186             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40187                     'before', true);
40188             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40189             // prevent input submission
40190             this.hiddenName = this.name;
40191         }
40192             
40193             
40194     },
40195     
40196     // private
40197     validateValue : function(value)
40198     {
40199         value = this.formatDate(value);
40200         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40201             Roo.log('super failed');
40202             return false;
40203         }
40204         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40205              return true;
40206         }
40207         var svalue = value;
40208         value = this.parseDate(value);
40209         if(!value){
40210             Roo.log('parse date failed' + svalue);
40211             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40212             return false;
40213         }
40214         var time = value.getTime();
40215         if(this.minValue && time < this.minValue.getTime()){
40216             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40217             return false;
40218         }
40219         if(this.maxValue && time > this.maxValue.getTime()){
40220             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40221             return false;
40222         }
40223         if(this.disabledDays){
40224             var day = value.getDay();
40225             for(var i = 0; i < this.disabledDays.length; i++) {
40226                 if(day === this.disabledDays[i]){
40227                     this.markInvalid(this.disabledDaysText);
40228                     return false;
40229                 }
40230             }
40231         }
40232         var fvalue = this.formatDate(value);
40233         if(this.ddMatch && this.ddMatch.test(fvalue)){
40234             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40235             return false;
40236         }
40237         return true;
40238     },
40239
40240     // private
40241     // Provides logic to override the default TriggerField.validateBlur which just returns true
40242     validateBlur : function(){
40243         return !this.menu || !this.menu.isVisible();
40244     },
40245     
40246     getName: function()
40247     {
40248         // returns hidden if it's set..
40249         if (!this.rendered) {return ''};
40250         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40251         
40252     },
40253
40254     /**
40255      * Returns the current date value of the date field.
40256      * @return {Date} The date value
40257      */
40258     getValue : function(){
40259         
40260         return  this.hiddenField ?
40261                 this.hiddenField.value :
40262                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40263     },
40264
40265     /**
40266      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40267      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40268      * (the default format used is "m/d/y").
40269      * <br />Usage:
40270      * <pre><code>
40271 //All of these calls set the same date value (May 4, 2006)
40272
40273 //Pass a date object:
40274 var dt = new Date('5/4/06');
40275 dateField.setValue(dt);
40276
40277 //Pass a date string (default format):
40278 dateField.setValue('5/4/06');
40279
40280 //Pass a date string (custom format):
40281 dateField.format = 'Y-m-d';
40282 dateField.setValue('2006-5-4');
40283 </code></pre>
40284      * @param {String/Date} date The date or valid date string
40285      */
40286     setValue : function(date){
40287         if (this.hiddenField) {
40288             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40289         }
40290         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40291         // make sure the value field is always stored as a date..
40292         this.value = this.parseDate(date);
40293         
40294         
40295     },
40296
40297     // private
40298     parseDate : function(value){
40299         if(!value || value instanceof Date){
40300             return value;
40301         }
40302         var v = Date.parseDate(value, this.format);
40303          if (!v && this.useIso) {
40304             v = Date.parseDate(value, 'Y-m-d');
40305         }
40306         if(!v && this.altFormats){
40307             if(!this.altFormatsArray){
40308                 this.altFormatsArray = this.altFormats.split("|");
40309             }
40310             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40311                 v = Date.parseDate(value, this.altFormatsArray[i]);
40312             }
40313         }
40314         return v;
40315     },
40316
40317     // private
40318     formatDate : function(date, fmt){
40319         return (!date || !(date instanceof Date)) ?
40320                date : date.dateFormat(fmt || this.format);
40321     },
40322
40323     // private
40324     menuListeners : {
40325         select: function(m, d){
40326             
40327             this.setValue(d);
40328             this.fireEvent('select', this, d);
40329         },
40330         show : function(){ // retain focus styling
40331             this.onFocus();
40332         },
40333         hide : function(){
40334             this.focus.defer(10, this);
40335             var ml = this.menuListeners;
40336             this.menu.un("select", ml.select,  this);
40337             this.menu.un("show", ml.show,  this);
40338             this.menu.un("hide", ml.hide,  this);
40339         }
40340     },
40341
40342     // private
40343     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40344     onTriggerClick : function(){
40345         if(this.disabled){
40346             return;
40347         }
40348         if(this.menu == null){
40349             this.menu = new Roo.menu.DateMenu();
40350         }
40351         Roo.apply(this.menu.picker,  {
40352             showClear: this.allowBlank,
40353             minDate : this.minValue,
40354             maxDate : this.maxValue,
40355             disabledDatesRE : this.ddMatch,
40356             disabledDatesText : this.disabledDatesText,
40357             disabledDays : this.disabledDays,
40358             disabledDaysText : this.disabledDaysText,
40359             format : this.useIso ? 'Y-m-d' : this.format,
40360             minText : String.format(this.minText, this.formatDate(this.minValue)),
40361             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40362         });
40363         this.menu.on(Roo.apply({}, this.menuListeners, {
40364             scope:this
40365         }));
40366         this.menu.picker.setValue(this.getValue() || new Date());
40367         this.menu.show(this.el, "tl-bl?");
40368     },
40369
40370     beforeBlur : function(){
40371         var v = this.parseDate(this.getRawValue());
40372         if(v){
40373             this.setValue(v);
40374         }
40375     },
40376
40377     /*@
40378      * overide
40379      * 
40380      */
40381     isDirty : function() {
40382         if(this.disabled) {
40383             return false;
40384         }
40385         
40386         if(typeof(this.startValue) === 'undefined'){
40387             return false;
40388         }
40389         
40390         return String(this.getValue()) !== String(this.startValue);
40391         
40392     }
40393 });/*
40394  * Based on:
40395  * Ext JS Library 1.1.1
40396  * Copyright(c) 2006-2007, Ext JS, LLC.
40397  *
40398  * Originally Released Under LGPL - original licence link has changed is not relivant.
40399  *
40400  * Fork - LGPL
40401  * <script type="text/javascript">
40402  */
40403  
40404 /**
40405  * @class Roo.form.MonthField
40406  * @extends Roo.form.TriggerField
40407  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40408 * @constructor
40409 * Create a new MonthField
40410 * @param {Object} config
40411  */
40412 Roo.form.MonthField = function(config){
40413     
40414     Roo.form.MonthField.superclass.constructor.call(this, config);
40415     
40416       this.addEvents({
40417          
40418         /**
40419          * @event select
40420          * Fires when a date is selected
40421              * @param {Roo.form.MonthFieeld} combo This combo box
40422              * @param {Date} date The date selected
40423              */
40424         'select' : true
40425          
40426     });
40427     
40428     
40429     if(typeof this.minValue == "string") {
40430         this.minValue = this.parseDate(this.minValue);
40431     }
40432     if(typeof this.maxValue == "string") {
40433         this.maxValue = this.parseDate(this.maxValue);
40434     }
40435     this.ddMatch = null;
40436     if(this.disabledDates){
40437         var dd = this.disabledDates;
40438         var re = "(?:";
40439         for(var i = 0; i < dd.length; i++){
40440             re += dd[i];
40441             if(i != dd.length-1) {
40442                 re += "|";
40443             }
40444         }
40445         this.ddMatch = new RegExp(re + ")");
40446     }
40447 };
40448
40449 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40450     /**
40451      * @cfg {String} format
40452      * The default date format string which can be overriden for localization support.  The format must be
40453      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40454      */
40455     format : "M Y",
40456     /**
40457      * @cfg {String} altFormats
40458      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40459      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40460      */
40461     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40462     /**
40463      * @cfg {Array} disabledDays
40464      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40465      */
40466     disabledDays : [0,1,2,3,4,5,6],
40467     /**
40468      * @cfg {String} disabledDaysText
40469      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40470      */
40471     disabledDaysText : "Disabled",
40472     /**
40473      * @cfg {Array} disabledDates
40474      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40475      * expression so they are very powerful. Some examples:
40476      * <ul>
40477      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40478      * <li>["03/08", "09/16"] would disable those days for every year</li>
40479      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40480      * <li>["03/../2006"] would disable every day in March 2006</li>
40481      * <li>["^03"] would disable every day in every March</li>
40482      * </ul>
40483      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40484      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40485      */
40486     disabledDates : null,
40487     /**
40488      * @cfg {String} disabledDatesText
40489      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40490      */
40491     disabledDatesText : "Disabled",
40492     /**
40493      * @cfg {Date/String} minValue
40494      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40495      * valid format (defaults to null).
40496      */
40497     minValue : null,
40498     /**
40499      * @cfg {Date/String} maxValue
40500      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40501      * valid format (defaults to null).
40502      */
40503     maxValue : null,
40504     /**
40505      * @cfg {String} minText
40506      * The error text to display when the date in the cell is before minValue (defaults to
40507      * 'The date in this field must be after {minValue}').
40508      */
40509     minText : "The date in this field must be equal to or after {0}",
40510     /**
40511      * @cfg {String} maxTextf
40512      * The error text to display when the date in the cell is after maxValue (defaults to
40513      * 'The date in this field must be before {maxValue}').
40514      */
40515     maxText : "The date in this field must be equal to or before {0}",
40516     /**
40517      * @cfg {String} invalidText
40518      * The error text to display when the date in the field is invalid (defaults to
40519      * '{value} is not a valid date - it must be in the format {format}').
40520      */
40521     invalidText : "{0} is not a valid date - it must be in the format {1}",
40522     /**
40523      * @cfg {String} triggerClass
40524      * An additional CSS class used to style the trigger button.  The trigger will always get the
40525      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40526      * which displays a calendar icon).
40527      */
40528     triggerClass : 'x-form-date-trigger',
40529     
40530
40531     /**
40532      * @cfg {Boolean} useIso
40533      * if enabled, then the date field will use a hidden field to store the 
40534      * real value as iso formated date. default (true)
40535      */ 
40536     useIso : true,
40537     /**
40538      * @cfg {String/Object} autoCreate
40539      * A DomHelper element spec, or true for a default element spec (defaults to
40540      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40541      */ 
40542     // private
40543     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40544     
40545     // private
40546     hiddenField: false,
40547     
40548     hideMonthPicker : false,
40549     
40550     onRender : function(ct, position)
40551     {
40552         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40553         if (this.useIso) {
40554             this.el.dom.removeAttribute('name'); 
40555             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40556                     'before', true);
40557             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40558             // prevent input submission
40559             this.hiddenName = this.name;
40560         }
40561             
40562             
40563     },
40564     
40565     // private
40566     validateValue : function(value)
40567     {
40568         value = this.formatDate(value);
40569         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40570             return false;
40571         }
40572         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40573              return true;
40574         }
40575         var svalue = value;
40576         value = this.parseDate(value);
40577         if(!value){
40578             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40579             return false;
40580         }
40581         var time = value.getTime();
40582         if(this.minValue && time < this.minValue.getTime()){
40583             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40584             return false;
40585         }
40586         if(this.maxValue && time > this.maxValue.getTime()){
40587             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40588             return false;
40589         }
40590         /*if(this.disabledDays){
40591             var day = value.getDay();
40592             for(var i = 0; i < this.disabledDays.length; i++) {
40593                 if(day === this.disabledDays[i]){
40594                     this.markInvalid(this.disabledDaysText);
40595                     return false;
40596                 }
40597             }
40598         }
40599         */
40600         var fvalue = this.formatDate(value);
40601         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40602             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40603             return false;
40604         }
40605         */
40606         return true;
40607     },
40608
40609     // private
40610     // Provides logic to override the default TriggerField.validateBlur which just returns true
40611     validateBlur : function(){
40612         return !this.menu || !this.menu.isVisible();
40613     },
40614
40615     /**
40616      * Returns the current date value of the date field.
40617      * @return {Date} The date value
40618      */
40619     getValue : function(){
40620         
40621         
40622         
40623         return  this.hiddenField ?
40624                 this.hiddenField.value :
40625                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40626     },
40627
40628     /**
40629      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40630      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40631      * (the default format used is "m/d/y").
40632      * <br />Usage:
40633      * <pre><code>
40634 //All of these calls set the same date value (May 4, 2006)
40635
40636 //Pass a date object:
40637 var dt = new Date('5/4/06');
40638 monthField.setValue(dt);
40639
40640 //Pass a date string (default format):
40641 monthField.setValue('5/4/06');
40642
40643 //Pass a date string (custom format):
40644 monthField.format = 'Y-m-d';
40645 monthField.setValue('2006-5-4');
40646 </code></pre>
40647      * @param {String/Date} date The date or valid date string
40648      */
40649     setValue : function(date){
40650         Roo.log('month setValue' + date);
40651         // can only be first of month..
40652         
40653         var val = this.parseDate(date);
40654         
40655         if (this.hiddenField) {
40656             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40657         }
40658         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40659         this.value = this.parseDate(date);
40660     },
40661
40662     // private
40663     parseDate : function(value){
40664         if(!value || value instanceof Date){
40665             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40666             return value;
40667         }
40668         var v = Date.parseDate(value, this.format);
40669         if (!v && this.useIso) {
40670             v = Date.parseDate(value, 'Y-m-d');
40671         }
40672         if (v) {
40673             // 
40674             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40675         }
40676         
40677         
40678         if(!v && this.altFormats){
40679             if(!this.altFormatsArray){
40680                 this.altFormatsArray = this.altFormats.split("|");
40681             }
40682             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40683                 v = Date.parseDate(value, this.altFormatsArray[i]);
40684             }
40685         }
40686         return v;
40687     },
40688
40689     // private
40690     formatDate : function(date, fmt){
40691         return (!date || !(date instanceof Date)) ?
40692                date : date.dateFormat(fmt || this.format);
40693     },
40694
40695     // private
40696     menuListeners : {
40697         select: function(m, d){
40698             this.setValue(d);
40699             this.fireEvent('select', this, d);
40700         },
40701         show : function(){ // retain focus styling
40702             this.onFocus();
40703         },
40704         hide : function(){
40705             this.focus.defer(10, this);
40706             var ml = this.menuListeners;
40707             this.menu.un("select", ml.select,  this);
40708             this.menu.un("show", ml.show,  this);
40709             this.menu.un("hide", ml.hide,  this);
40710         }
40711     },
40712     // private
40713     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40714     onTriggerClick : function(){
40715         if(this.disabled){
40716             return;
40717         }
40718         if(this.menu == null){
40719             this.menu = new Roo.menu.DateMenu();
40720            
40721         }
40722         
40723         Roo.apply(this.menu.picker,  {
40724             
40725             showClear: this.allowBlank,
40726             minDate : this.minValue,
40727             maxDate : this.maxValue,
40728             disabledDatesRE : this.ddMatch,
40729             disabledDatesText : this.disabledDatesText,
40730             
40731             format : this.useIso ? 'Y-m-d' : this.format,
40732             minText : String.format(this.minText, this.formatDate(this.minValue)),
40733             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40734             
40735         });
40736          this.menu.on(Roo.apply({}, this.menuListeners, {
40737             scope:this
40738         }));
40739        
40740         
40741         var m = this.menu;
40742         var p = m.picker;
40743         
40744         // hide month picker get's called when we called by 'before hide';
40745         
40746         var ignorehide = true;
40747         p.hideMonthPicker  = function(disableAnim){
40748             if (ignorehide) {
40749                 return;
40750             }
40751              if(this.monthPicker){
40752                 Roo.log("hideMonthPicker called");
40753                 if(disableAnim === true){
40754                     this.monthPicker.hide();
40755                 }else{
40756                     this.monthPicker.slideOut('t', {duration:.2});
40757                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40758                     p.fireEvent("select", this, this.value);
40759                     m.hide();
40760                 }
40761             }
40762         }
40763         
40764         Roo.log('picker set value');
40765         Roo.log(this.getValue());
40766         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40767         m.show(this.el, 'tl-bl?');
40768         ignorehide  = false;
40769         // this will trigger hideMonthPicker..
40770         
40771         
40772         // hidden the day picker
40773         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40774         
40775         
40776         
40777       
40778         
40779         p.showMonthPicker.defer(100, p);
40780     
40781         
40782        
40783     },
40784
40785     beforeBlur : function(){
40786         var v = this.parseDate(this.getRawValue());
40787         if(v){
40788             this.setValue(v);
40789         }
40790     }
40791
40792     /** @cfg {Boolean} grow @hide */
40793     /** @cfg {Number} growMin @hide */
40794     /** @cfg {Number} growMax @hide */
40795     /**
40796      * @hide
40797      * @method autoSize
40798      */
40799 });/*
40800  * Based on:
40801  * Ext JS Library 1.1.1
40802  * Copyright(c) 2006-2007, Ext JS, LLC.
40803  *
40804  * Originally Released Under LGPL - original licence link has changed is not relivant.
40805  *
40806  * Fork - LGPL
40807  * <script type="text/javascript">
40808  */
40809  
40810
40811 /**
40812  * @class Roo.form.ComboBox
40813  * @extends Roo.form.TriggerField
40814  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40815  * @constructor
40816  * Create a new ComboBox.
40817  * @param {Object} config Configuration options
40818  */
40819 Roo.form.ComboBox = function(config){
40820     Roo.form.ComboBox.superclass.constructor.call(this, config);
40821     this.addEvents({
40822         /**
40823          * @event expand
40824          * Fires when the dropdown list is expanded
40825              * @param {Roo.form.ComboBox} combo This combo box
40826              */
40827         'expand' : true,
40828         /**
40829          * @event collapse
40830          * Fires when the dropdown list is collapsed
40831              * @param {Roo.form.ComboBox} combo This combo box
40832              */
40833         'collapse' : true,
40834         /**
40835          * @event beforeselect
40836          * Fires before a list item is selected. Return false to cancel the selection.
40837              * @param {Roo.form.ComboBox} combo This combo box
40838              * @param {Roo.data.Record} record The data record returned from the underlying store
40839              * @param {Number} index The index of the selected item in the dropdown list
40840              */
40841         'beforeselect' : true,
40842         /**
40843          * @event select
40844          * Fires when a list item is selected
40845              * @param {Roo.form.ComboBox} combo This combo box
40846              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40847              * @param {Number} index The index of the selected item in the dropdown list
40848              */
40849         'select' : true,
40850         /**
40851          * @event beforequery
40852          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40853          * The event object passed has these properties:
40854              * @param {Roo.form.ComboBox} combo This combo box
40855              * @param {String} query The query
40856              * @param {Boolean} forceAll true to force "all" query
40857              * @param {Boolean} cancel true to cancel the query
40858              * @param {Object} e The query event object
40859              */
40860         'beforequery': true,
40861          /**
40862          * @event add
40863          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40864              * @param {Roo.form.ComboBox} combo This combo box
40865              */
40866         'add' : true,
40867         /**
40868          * @event edit
40869          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40870              * @param {Roo.form.ComboBox} combo This combo box
40871              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40872              */
40873         'edit' : true
40874         
40875         
40876     });
40877     if(this.transform){
40878         this.allowDomMove = false;
40879         var s = Roo.getDom(this.transform);
40880         if(!this.hiddenName){
40881             this.hiddenName = s.name;
40882         }
40883         if(!this.store){
40884             this.mode = 'local';
40885             var d = [], opts = s.options;
40886             for(var i = 0, len = opts.length;i < len; i++){
40887                 var o = opts[i];
40888                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40889                 if(o.selected) {
40890                     this.value = value;
40891                 }
40892                 d.push([value, o.text]);
40893             }
40894             this.store = new Roo.data.SimpleStore({
40895                 'id': 0,
40896                 fields: ['value', 'text'],
40897                 data : d
40898             });
40899             this.valueField = 'value';
40900             this.displayField = 'text';
40901         }
40902         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40903         if(!this.lazyRender){
40904             this.target = true;
40905             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40906             s.parentNode.removeChild(s); // remove it
40907             this.render(this.el.parentNode);
40908         }else{
40909             s.parentNode.removeChild(s); // remove it
40910         }
40911
40912     }
40913     if (this.store) {
40914         this.store = Roo.factory(this.store, Roo.data);
40915     }
40916     
40917     this.selectedIndex = -1;
40918     if(this.mode == 'local'){
40919         if(config.queryDelay === undefined){
40920             this.queryDelay = 10;
40921         }
40922         if(config.minChars === undefined){
40923             this.minChars = 0;
40924         }
40925     }
40926 };
40927
40928 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40929     /**
40930      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40931      */
40932     /**
40933      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40934      * rendering into an Roo.Editor, defaults to false)
40935      */
40936     /**
40937      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40938      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40939      */
40940     /**
40941      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40942      */
40943     /**
40944      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40945      * the dropdown list (defaults to undefined, with no header element)
40946      */
40947
40948      /**
40949      * @cfg {String/Roo.Template} tpl The template to use to render the output
40950      */
40951      
40952     // private
40953     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40954     /**
40955      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40956      */
40957     listWidth: undefined,
40958     /**
40959      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40960      * mode = 'remote' or 'text' if mode = 'local')
40961      */
40962     displayField: undefined,
40963     /**
40964      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40965      * mode = 'remote' or 'value' if mode = 'local'). 
40966      * Note: use of a valueField requires the user make a selection
40967      * in order for a value to be mapped.
40968      */
40969     valueField: undefined,
40970     
40971     
40972     /**
40973      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40974      * field's data value (defaults to the underlying DOM element's name)
40975      */
40976     hiddenName: undefined,
40977     /**
40978      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40979      */
40980     listClass: '',
40981     /**
40982      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40983      */
40984     selectedClass: 'x-combo-selected',
40985     /**
40986      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40987      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40988      * which displays a downward arrow icon).
40989      */
40990     triggerClass : 'x-form-arrow-trigger',
40991     /**
40992      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40993      */
40994     shadow:'sides',
40995     /**
40996      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40997      * anchor positions (defaults to 'tl-bl')
40998      */
40999     listAlign: 'tl-bl?',
41000     /**
41001      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41002      */
41003     maxHeight: 300,
41004     /**
41005      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41006      * query specified by the allQuery config option (defaults to 'query')
41007      */
41008     triggerAction: 'query',
41009     /**
41010      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41011      * (defaults to 4, does not apply if editable = false)
41012      */
41013     minChars : 4,
41014     /**
41015      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41016      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41017      */
41018     typeAhead: false,
41019     /**
41020      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41021      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41022      */
41023     queryDelay: 500,
41024     /**
41025      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41026      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41027      */
41028     pageSize: 0,
41029     /**
41030      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41031      * when editable = true (defaults to false)
41032      */
41033     selectOnFocus:false,
41034     /**
41035      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41036      */
41037     queryParam: 'query',
41038     /**
41039      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41040      * when mode = 'remote' (defaults to 'Loading...')
41041      */
41042     loadingText: 'Loading...',
41043     /**
41044      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41045      */
41046     resizable: false,
41047     /**
41048      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41049      */
41050     handleHeight : 8,
41051     /**
41052      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41053      * traditional select (defaults to true)
41054      */
41055     editable: true,
41056     /**
41057      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41058      */
41059     allQuery: '',
41060     /**
41061      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41062      */
41063     mode: 'remote',
41064     /**
41065      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41066      * listWidth has a higher value)
41067      */
41068     minListWidth : 70,
41069     /**
41070      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41071      * allow the user to set arbitrary text into the field (defaults to false)
41072      */
41073     forceSelection:false,
41074     /**
41075      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41076      * if typeAhead = true (defaults to 250)
41077      */
41078     typeAheadDelay : 250,
41079     /**
41080      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41081      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41082      */
41083     valueNotFoundText : undefined,
41084     /**
41085      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41086      */
41087     blockFocus : false,
41088     
41089     /**
41090      * @cfg {Boolean} disableClear Disable showing of clear button.
41091      */
41092     disableClear : false,
41093     /**
41094      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41095      */
41096     alwaysQuery : false,
41097     
41098     //private
41099     addicon : false,
41100     editicon: false,
41101     
41102     // element that contains real text value.. (when hidden is used..)
41103      
41104     // private
41105     onRender : function(ct, position){
41106         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41107         if(this.hiddenName){
41108             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41109                     'before', true);
41110             this.hiddenField.value =
41111                 this.hiddenValue !== undefined ? this.hiddenValue :
41112                 this.value !== undefined ? this.value : '';
41113
41114             // prevent input submission
41115             this.el.dom.removeAttribute('name');
41116              
41117              
41118         }
41119         if(Roo.isGecko){
41120             this.el.dom.setAttribute('autocomplete', 'off');
41121         }
41122
41123         var cls = 'x-combo-list';
41124
41125         this.list = new Roo.Layer({
41126             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41127         });
41128
41129         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41130         this.list.setWidth(lw);
41131         this.list.swallowEvent('mousewheel');
41132         this.assetHeight = 0;
41133
41134         if(this.title){
41135             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41136             this.assetHeight += this.header.getHeight();
41137         }
41138
41139         this.innerList = this.list.createChild({cls:cls+'-inner'});
41140         this.innerList.on('mouseover', this.onViewOver, this);
41141         this.innerList.on('mousemove', this.onViewMove, this);
41142         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41143         
41144         if(this.allowBlank && !this.pageSize && !this.disableClear){
41145             this.footer = this.list.createChild({cls:cls+'-ft'});
41146             this.pageTb = new Roo.Toolbar(this.footer);
41147            
41148         }
41149         if(this.pageSize){
41150             this.footer = this.list.createChild({cls:cls+'-ft'});
41151             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41152                     {pageSize: this.pageSize});
41153             
41154         }
41155         
41156         if (this.pageTb && this.allowBlank && !this.disableClear) {
41157             var _this = this;
41158             this.pageTb.add(new Roo.Toolbar.Fill(), {
41159                 cls: 'x-btn-icon x-btn-clear',
41160                 text: '&#160;',
41161                 handler: function()
41162                 {
41163                     _this.collapse();
41164                     _this.clearValue();
41165                     _this.onSelect(false, -1);
41166                 }
41167             });
41168         }
41169         if (this.footer) {
41170             this.assetHeight += this.footer.getHeight();
41171         }
41172         
41173
41174         if(!this.tpl){
41175             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41176         }
41177
41178         this.view = new Roo.View(this.innerList, this.tpl, {
41179             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41180         });
41181
41182         this.view.on('click', this.onViewClick, this);
41183
41184         this.store.on('beforeload', this.onBeforeLoad, this);
41185         this.store.on('load', this.onLoad, this);
41186         this.store.on('loadexception', this.onLoadException, this);
41187
41188         if(this.resizable){
41189             this.resizer = new Roo.Resizable(this.list,  {
41190                pinned:true, handles:'se'
41191             });
41192             this.resizer.on('resize', function(r, w, h){
41193                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41194                 this.listWidth = w;
41195                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41196                 this.restrictHeight();
41197             }, this);
41198             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41199         }
41200         if(!this.editable){
41201             this.editable = true;
41202             this.setEditable(false);
41203         }  
41204         
41205         
41206         if (typeof(this.events.add.listeners) != 'undefined') {
41207             
41208             this.addicon = this.wrap.createChild(
41209                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41210        
41211             this.addicon.on('click', function(e) {
41212                 this.fireEvent('add', this);
41213             }, this);
41214         }
41215         if (typeof(this.events.edit.listeners) != 'undefined') {
41216             
41217             this.editicon = this.wrap.createChild(
41218                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41219             if (this.addicon) {
41220                 this.editicon.setStyle('margin-left', '40px');
41221             }
41222             this.editicon.on('click', function(e) {
41223                 
41224                 // we fire even  if inothing is selected..
41225                 this.fireEvent('edit', this, this.lastData );
41226                 
41227             }, this);
41228         }
41229         
41230         
41231         
41232     },
41233
41234     // private
41235     initEvents : function(){
41236         Roo.form.ComboBox.superclass.initEvents.call(this);
41237
41238         this.keyNav = new Roo.KeyNav(this.el, {
41239             "up" : function(e){
41240                 this.inKeyMode = true;
41241                 this.selectPrev();
41242             },
41243
41244             "down" : function(e){
41245                 if(!this.isExpanded()){
41246                     this.onTriggerClick();
41247                 }else{
41248                     this.inKeyMode = true;
41249                     this.selectNext();
41250                 }
41251             },
41252
41253             "enter" : function(e){
41254                 this.onViewClick();
41255                 //return true;
41256             },
41257
41258             "esc" : function(e){
41259                 this.collapse();
41260             },
41261
41262             "tab" : function(e){
41263                 this.onViewClick(false);
41264                 this.fireEvent("specialkey", this, e);
41265                 return true;
41266             },
41267
41268             scope : this,
41269
41270             doRelay : function(foo, bar, hname){
41271                 if(hname == 'down' || this.scope.isExpanded()){
41272                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41273                 }
41274                 return true;
41275             },
41276
41277             forceKeyDown: true
41278         });
41279         this.queryDelay = Math.max(this.queryDelay || 10,
41280                 this.mode == 'local' ? 10 : 250);
41281         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41282         if(this.typeAhead){
41283             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41284         }
41285         if(this.editable !== false){
41286             this.el.on("keyup", this.onKeyUp, this);
41287         }
41288         if(this.forceSelection){
41289             this.on('blur', this.doForce, this);
41290         }
41291     },
41292
41293     onDestroy : function(){
41294         if(this.view){
41295             this.view.setStore(null);
41296             this.view.el.removeAllListeners();
41297             this.view.el.remove();
41298             this.view.purgeListeners();
41299         }
41300         if(this.list){
41301             this.list.destroy();
41302         }
41303         if(this.store){
41304             this.store.un('beforeload', this.onBeforeLoad, this);
41305             this.store.un('load', this.onLoad, this);
41306             this.store.un('loadexception', this.onLoadException, this);
41307         }
41308         Roo.form.ComboBox.superclass.onDestroy.call(this);
41309     },
41310
41311     // private
41312     fireKey : function(e){
41313         if(e.isNavKeyPress() && !this.list.isVisible()){
41314             this.fireEvent("specialkey", this, e);
41315         }
41316     },
41317
41318     // private
41319     onResize: function(w, h){
41320         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41321         
41322         if(typeof w != 'number'){
41323             // we do not handle it!?!?
41324             return;
41325         }
41326         var tw = this.trigger.getWidth();
41327         tw += this.addicon ? this.addicon.getWidth() : 0;
41328         tw += this.editicon ? this.editicon.getWidth() : 0;
41329         var x = w - tw;
41330         this.el.setWidth( this.adjustWidth('input', x));
41331             
41332         this.trigger.setStyle('left', x+'px');
41333         
41334         if(this.list && this.listWidth === undefined){
41335             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41336             this.list.setWidth(lw);
41337             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41338         }
41339         
41340     
41341         
41342     },
41343
41344     /**
41345      * Allow or prevent the user from directly editing the field text.  If false is passed,
41346      * the user will only be able to select from the items defined in the dropdown list.  This method
41347      * is the runtime equivalent of setting the 'editable' config option at config time.
41348      * @param {Boolean} value True to allow the user to directly edit the field text
41349      */
41350     setEditable : function(value){
41351         if(value == this.editable){
41352             return;
41353         }
41354         this.editable = value;
41355         if(!value){
41356             this.el.dom.setAttribute('readOnly', true);
41357             this.el.on('mousedown', this.onTriggerClick,  this);
41358             this.el.addClass('x-combo-noedit');
41359         }else{
41360             this.el.dom.setAttribute('readOnly', false);
41361             this.el.un('mousedown', this.onTriggerClick,  this);
41362             this.el.removeClass('x-combo-noedit');
41363         }
41364     },
41365
41366     // private
41367     onBeforeLoad : function(){
41368         if(!this.hasFocus){
41369             return;
41370         }
41371         this.innerList.update(this.loadingText ?
41372                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41373         this.restrictHeight();
41374         this.selectedIndex = -1;
41375     },
41376
41377     // private
41378     onLoad : function(){
41379         if(!this.hasFocus){
41380             return;
41381         }
41382         if(this.store.getCount() > 0){
41383             this.expand();
41384             this.restrictHeight();
41385             if(this.lastQuery == this.allQuery){
41386                 if(this.editable){
41387                     this.el.dom.select();
41388                 }
41389                 if(!this.selectByValue(this.value, true)){
41390                     this.select(0, true);
41391                 }
41392             }else{
41393                 this.selectNext();
41394                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41395                     this.taTask.delay(this.typeAheadDelay);
41396                 }
41397             }
41398         }else{
41399             this.onEmptyResults();
41400         }
41401         //this.el.focus();
41402     },
41403     // private
41404     onLoadException : function()
41405     {
41406         this.collapse();
41407         Roo.log(this.store.reader.jsonData);
41408         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41409             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41410         }
41411         
41412         
41413     },
41414     // private
41415     onTypeAhead : function(){
41416         if(this.store.getCount() > 0){
41417             var r = this.store.getAt(0);
41418             var newValue = r.data[this.displayField];
41419             var len = newValue.length;
41420             var selStart = this.getRawValue().length;
41421             if(selStart != len){
41422                 this.setRawValue(newValue);
41423                 this.selectText(selStart, newValue.length);
41424             }
41425         }
41426     },
41427
41428     // private
41429     onSelect : function(record, index){
41430         if(this.fireEvent('beforeselect', this, record, index) !== false){
41431             this.setFromData(index > -1 ? record.data : false);
41432             this.collapse();
41433             this.fireEvent('select', this, record, index);
41434         }
41435     },
41436
41437     /**
41438      * Returns the currently selected field value or empty string if no value is set.
41439      * @return {String} value The selected value
41440      */
41441     getValue : function(){
41442         if(this.valueField){
41443             return typeof this.value != 'undefined' ? this.value : '';
41444         }
41445         return Roo.form.ComboBox.superclass.getValue.call(this);
41446     },
41447
41448     /**
41449      * Clears any text/value currently set in the field
41450      */
41451     clearValue : function(){
41452         if(this.hiddenField){
41453             this.hiddenField.value = '';
41454         }
41455         this.value = '';
41456         this.setRawValue('');
41457         this.lastSelectionText = '';
41458         
41459     },
41460
41461     /**
41462      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41463      * will be displayed in the field.  If the value does not match the data value of an existing item,
41464      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41465      * Otherwise the field will be blank (although the value will still be set).
41466      * @param {String} value The value to match
41467      */
41468     setValue : function(v){
41469         var text = v;
41470         if(this.valueField){
41471             var r = this.findRecord(this.valueField, v);
41472             if(r){
41473                 text = r.data[this.displayField];
41474             }else if(this.valueNotFoundText !== undefined){
41475                 text = this.valueNotFoundText;
41476             }
41477         }
41478         this.lastSelectionText = text;
41479         if(this.hiddenField){
41480             this.hiddenField.value = v;
41481         }
41482         Roo.form.ComboBox.superclass.setValue.call(this, text);
41483         this.value = v;
41484     },
41485     /**
41486      * @property {Object} the last set data for the element
41487      */
41488     
41489     lastData : false,
41490     /**
41491      * Sets the value of the field based on a object which is related to the record format for the store.
41492      * @param {Object} value the value to set as. or false on reset?
41493      */
41494     setFromData : function(o){
41495         var dv = ''; // display value
41496         var vv = ''; // value value..
41497         this.lastData = o;
41498         if (this.displayField) {
41499             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41500         } else {
41501             // this is an error condition!!!
41502             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41503         }
41504         
41505         if(this.valueField){
41506             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41507         }
41508         if(this.hiddenField){
41509             this.hiddenField.value = vv;
41510             
41511             this.lastSelectionText = dv;
41512             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41513             this.value = vv;
41514             return;
41515         }
41516         // no hidden field.. - we store the value in 'value', but still display
41517         // display field!!!!
41518         this.lastSelectionText = dv;
41519         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41520         this.value = vv;
41521         
41522         
41523     },
41524     // private
41525     reset : function(){
41526         // overridden so that last data is reset..
41527         this.setValue(this.resetValue);
41528         this.clearInvalid();
41529         this.lastData = false;
41530         if (this.view) {
41531             this.view.clearSelections();
41532         }
41533     },
41534     // private
41535     findRecord : function(prop, value){
41536         var record;
41537         if(this.store.getCount() > 0){
41538             this.store.each(function(r){
41539                 if(r.data[prop] == value){
41540                     record = r;
41541                     return false;
41542                 }
41543                 return true;
41544             });
41545         }
41546         return record;
41547     },
41548     
41549     getName: function()
41550     {
41551         // returns hidden if it's set..
41552         if (!this.rendered) {return ''};
41553         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41554         
41555     },
41556     // private
41557     onViewMove : function(e, t){
41558         this.inKeyMode = false;
41559     },
41560
41561     // private
41562     onViewOver : function(e, t){
41563         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41564             return;
41565         }
41566         var item = this.view.findItemFromChild(t);
41567         if(item){
41568             var index = this.view.indexOf(item);
41569             this.select(index, false);
41570         }
41571     },
41572
41573     // private
41574     onViewClick : function(doFocus)
41575     {
41576         var index = this.view.getSelectedIndexes()[0];
41577         var r = this.store.getAt(index);
41578         if(r){
41579             this.onSelect(r, index);
41580         }
41581         if(doFocus !== false && !this.blockFocus){
41582             this.el.focus();
41583         }
41584     },
41585
41586     // private
41587     restrictHeight : function(){
41588         this.innerList.dom.style.height = '';
41589         var inner = this.innerList.dom;
41590         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41591         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41592         this.list.beginUpdate();
41593         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41594         this.list.alignTo(this.el, this.listAlign);
41595         this.list.endUpdate();
41596     },
41597
41598     // private
41599     onEmptyResults : function(){
41600         this.collapse();
41601     },
41602
41603     /**
41604      * Returns true if the dropdown list is expanded, else false.
41605      */
41606     isExpanded : function(){
41607         return this.list.isVisible();
41608     },
41609
41610     /**
41611      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41612      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41613      * @param {String} value The data value of the item to select
41614      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41615      * selected item if it is not currently in view (defaults to true)
41616      * @return {Boolean} True if the value matched an item in the list, else false
41617      */
41618     selectByValue : function(v, scrollIntoView){
41619         if(v !== undefined && v !== null){
41620             var r = this.findRecord(this.valueField || this.displayField, v);
41621             if(r){
41622                 this.select(this.store.indexOf(r), scrollIntoView);
41623                 return true;
41624             }
41625         }
41626         return false;
41627     },
41628
41629     /**
41630      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41631      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41632      * @param {Number} index The zero-based index of the list item to select
41633      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41634      * selected item if it is not currently in view (defaults to true)
41635      */
41636     select : function(index, scrollIntoView){
41637         this.selectedIndex = index;
41638         this.view.select(index);
41639         if(scrollIntoView !== false){
41640             var el = this.view.getNode(index);
41641             if(el){
41642                 this.innerList.scrollChildIntoView(el, false);
41643             }
41644         }
41645     },
41646
41647     // private
41648     selectNext : function(){
41649         var ct = this.store.getCount();
41650         if(ct > 0){
41651             if(this.selectedIndex == -1){
41652                 this.select(0);
41653             }else if(this.selectedIndex < ct-1){
41654                 this.select(this.selectedIndex+1);
41655             }
41656         }
41657     },
41658
41659     // private
41660     selectPrev : function(){
41661         var ct = this.store.getCount();
41662         if(ct > 0){
41663             if(this.selectedIndex == -1){
41664                 this.select(0);
41665             }else if(this.selectedIndex != 0){
41666                 this.select(this.selectedIndex-1);
41667             }
41668         }
41669     },
41670
41671     // private
41672     onKeyUp : function(e){
41673         if(this.editable !== false && !e.isSpecialKey()){
41674             this.lastKey = e.getKey();
41675             this.dqTask.delay(this.queryDelay);
41676         }
41677     },
41678
41679     // private
41680     validateBlur : function(){
41681         return !this.list || !this.list.isVisible();   
41682     },
41683
41684     // private
41685     initQuery : function(){
41686         this.doQuery(this.getRawValue());
41687     },
41688
41689     // private
41690     doForce : function(){
41691         if(this.el.dom.value.length > 0){
41692             this.el.dom.value =
41693                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41694              
41695         }
41696     },
41697
41698     /**
41699      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41700      * query allowing the query action to be canceled if needed.
41701      * @param {String} query The SQL query to execute
41702      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41703      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41704      * saved in the current store (defaults to false)
41705      */
41706     doQuery : function(q, forceAll){
41707         if(q === undefined || q === null){
41708             q = '';
41709         }
41710         var qe = {
41711             query: q,
41712             forceAll: forceAll,
41713             combo: this,
41714             cancel:false
41715         };
41716         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41717             return false;
41718         }
41719         q = qe.query;
41720         forceAll = qe.forceAll;
41721         if(forceAll === true || (q.length >= this.minChars)){
41722             if(this.lastQuery != q || this.alwaysQuery){
41723                 this.lastQuery = q;
41724                 if(this.mode == 'local'){
41725                     this.selectedIndex = -1;
41726                     if(forceAll){
41727                         this.store.clearFilter();
41728                     }else{
41729                         this.store.filter(this.displayField, q);
41730                     }
41731                     this.onLoad();
41732                 }else{
41733                     this.store.baseParams[this.queryParam] = q;
41734                     this.store.load({
41735                         params: this.getParams(q)
41736                     });
41737                     this.expand();
41738                 }
41739             }else{
41740                 this.selectedIndex = -1;
41741                 this.onLoad();   
41742             }
41743         }
41744     },
41745
41746     // private
41747     getParams : function(q){
41748         var p = {};
41749         //p[this.queryParam] = q;
41750         if(this.pageSize){
41751             p.start = 0;
41752             p.limit = this.pageSize;
41753         }
41754         return p;
41755     },
41756
41757     /**
41758      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41759      */
41760     collapse : function(){
41761         if(!this.isExpanded()){
41762             return;
41763         }
41764         this.list.hide();
41765         Roo.get(document).un('mousedown', this.collapseIf, this);
41766         Roo.get(document).un('mousewheel', this.collapseIf, this);
41767         if (!this.editable) {
41768             Roo.get(document).un('keydown', this.listKeyPress, this);
41769         }
41770         this.fireEvent('collapse', this);
41771     },
41772
41773     // private
41774     collapseIf : function(e){
41775         if(!e.within(this.wrap) && !e.within(this.list)){
41776             this.collapse();
41777         }
41778     },
41779
41780     /**
41781      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41782      */
41783     expand : function(){
41784         if(this.isExpanded() || !this.hasFocus){
41785             return;
41786         }
41787         this.list.alignTo(this.el, this.listAlign);
41788         this.list.show();
41789         Roo.get(document).on('mousedown', this.collapseIf, this);
41790         Roo.get(document).on('mousewheel', this.collapseIf, this);
41791         if (!this.editable) {
41792             Roo.get(document).on('keydown', this.listKeyPress, this);
41793         }
41794         
41795         this.fireEvent('expand', this);
41796     },
41797
41798     // private
41799     // Implements the default empty TriggerField.onTriggerClick function
41800     onTriggerClick : function(){
41801         if(this.disabled){
41802             return;
41803         }
41804         if(this.isExpanded()){
41805             this.collapse();
41806             if (!this.blockFocus) {
41807                 this.el.focus();
41808             }
41809             
41810         }else {
41811             this.hasFocus = true;
41812             if(this.triggerAction == 'all') {
41813                 this.doQuery(this.allQuery, true);
41814             } else {
41815                 this.doQuery(this.getRawValue());
41816             }
41817             if (!this.blockFocus) {
41818                 this.el.focus();
41819             }
41820         }
41821     },
41822     listKeyPress : function(e)
41823     {
41824         //Roo.log('listkeypress');
41825         // scroll to first matching element based on key pres..
41826         if (e.isSpecialKey()) {
41827             return false;
41828         }
41829         var k = String.fromCharCode(e.getKey()).toUpperCase();
41830         //Roo.log(k);
41831         var match  = false;
41832         var csel = this.view.getSelectedNodes();
41833         var cselitem = false;
41834         if (csel.length) {
41835             var ix = this.view.indexOf(csel[0]);
41836             cselitem  = this.store.getAt(ix);
41837             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41838                 cselitem = false;
41839             }
41840             
41841         }
41842         
41843         this.store.each(function(v) { 
41844             if (cselitem) {
41845                 // start at existing selection.
41846                 if (cselitem.id == v.id) {
41847                     cselitem = false;
41848                 }
41849                 return;
41850             }
41851                 
41852             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41853                 match = this.store.indexOf(v);
41854                 return false;
41855             }
41856         }, this);
41857         
41858         if (match === false) {
41859             return true; // no more action?
41860         }
41861         // scroll to?
41862         this.view.select(match);
41863         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41864         sn.scrollIntoView(sn.dom.parentNode, false);
41865     }
41866
41867     /** 
41868     * @cfg {Boolean} grow 
41869     * @hide 
41870     */
41871     /** 
41872     * @cfg {Number} growMin 
41873     * @hide 
41874     */
41875     /** 
41876     * @cfg {Number} growMax 
41877     * @hide 
41878     */
41879     /**
41880      * @hide
41881      * @method autoSize
41882      */
41883 });/*
41884  * Copyright(c) 2010-2012, Roo J Solutions Limited
41885  *
41886  * Licence LGPL
41887  *
41888  */
41889
41890 /**
41891  * @class Roo.form.ComboBoxArray
41892  * @extends Roo.form.TextField
41893  * A facebook style adder... for lists of email / people / countries  etc...
41894  * pick multiple items from a combo box, and shows each one.
41895  *
41896  *  Fred [x]  Brian [x]  [Pick another |v]
41897  *
41898  *
41899  *  For this to work: it needs various extra information
41900  *    - normal combo problay has
41901  *      name, hiddenName
41902  *    + displayField, valueField
41903  *
41904  *    For our purpose...
41905  *
41906  *
41907  *   If we change from 'extends' to wrapping...
41908  *   
41909  *  
41910  *
41911  
41912  
41913  * @constructor
41914  * Create a new ComboBoxArray.
41915  * @param {Object} config Configuration options
41916  */
41917  
41918
41919 Roo.form.ComboBoxArray = function(config)
41920 {
41921     this.addEvents({
41922         /**
41923          * @event beforeremove
41924          * Fires before remove the value from the list
41925              * @param {Roo.form.ComboBoxArray} _self This combo box array
41926              * @param {Roo.form.ComboBoxArray.Item} item removed item
41927              */
41928         'beforeremove' : true,
41929         /**
41930          * @event remove
41931          * Fires when remove the value from the list
41932              * @param {Roo.form.ComboBoxArray} _self This combo box array
41933              * @param {Roo.form.ComboBoxArray.Item} item removed item
41934              */
41935         'remove' : true
41936         
41937         
41938     });
41939     
41940     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41941     
41942     this.items = new Roo.util.MixedCollection(false);
41943     
41944     // construct the child combo...
41945     
41946     
41947     
41948     
41949    
41950     
41951 }
41952
41953  
41954 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41955
41956     /**
41957      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41958      */
41959     
41960     lastData : false,
41961     
41962     // behavies liek a hiddne field
41963     inputType:      'hidden',
41964     /**
41965      * @cfg {Number} width The width of the box that displays the selected element
41966      */ 
41967     width:          300,
41968
41969     
41970     
41971     /**
41972      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41973      */
41974     name : false,
41975     /**
41976      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41977      */
41978     hiddenName : false,
41979     
41980     
41981     // private the array of items that are displayed..
41982     items  : false,
41983     // private - the hidden field el.
41984     hiddenEl : false,
41985     // private - the filed el..
41986     el : false,
41987     
41988     //validateValue : function() { return true; }, // all values are ok!
41989     //onAddClick: function() { },
41990     
41991     onRender : function(ct, position) 
41992     {
41993         
41994         // create the standard hidden element
41995         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41996         
41997         
41998         // give fake names to child combo;
41999         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42000         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42001         
42002         this.combo = Roo.factory(this.combo, Roo.form);
42003         this.combo.onRender(ct, position);
42004         if (typeof(this.combo.width) != 'undefined') {
42005             this.combo.onResize(this.combo.width,0);
42006         }
42007         
42008         this.combo.initEvents();
42009         
42010         // assigned so form know we need to do this..
42011         this.store          = this.combo.store;
42012         this.valueField     = this.combo.valueField;
42013         this.displayField   = this.combo.displayField ;
42014         
42015         
42016         this.combo.wrap.addClass('x-cbarray-grp');
42017         
42018         var cbwrap = this.combo.wrap.createChild(
42019             {tag: 'div', cls: 'x-cbarray-cb'},
42020             this.combo.el.dom
42021         );
42022         
42023              
42024         this.hiddenEl = this.combo.wrap.createChild({
42025             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42026         });
42027         this.el = this.combo.wrap.createChild({
42028             tag: 'input',  type:'hidden' , name: this.name, value : ''
42029         });
42030          //   this.el.dom.removeAttribute("name");
42031         
42032         
42033         this.outerWrap = this.combo.wrap;
42034         this.wrap = cbwrap;
42035         
42036         this.outerWrap.setWidth(this.width);
42037         this.outerWrap.dom.removeChild(this.el.dom);
42038         
42039         this.wrap.dom.appendChild(this.el.dom);
42040         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42041         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42042         
42043         this.combo.trigger.setStyle('position','relative');
42044         this.combo.trigger.setStyle('left', '0px');
42045         this.combo.trigger.setStyle('top', '2px');
42046         
42047         this.combo.el.setStyle('vertical-align', 'text-bottom');
42048         
42049         //this.trigger.setStyle('vertical-align', 'top');
42050         
42051         // this should use the code from combo really... on('add' ....)
42052         if (this.adder) {
42053             
42054         
42055             this.adder = this.outerWrap.createChild(
42056                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42057             var _t = this;
42058             this.adder.on('click', function(e) {
42059                 _t.fireEvent('adderclick', this, e);
42060             }, _t);
42061         }
42062         //var _t = this;
42063         //this.adder.on('click', this.onAddClick, _t);
42064         
42065         
42066         this.combo.on('select', function(cb, rec, ix) {
42067             this.addItem(rec.data);
42068             
42069             cb.setValue('');
42070             cb.el.dom.value = '';
42071             //cb.lastData = rec.data;
42072             // add to list
42073             
42074         }, this);
42075         
42076         
42077     },
42078     
42079     
42080     getName: function()
42081     {
42082         // returns hidden if it's set..
42083         if (!this.rendered) {return ''};
42084         return  this.hiddenName ? this.hiddenName : this.name;
42085         
42086     },
42087     
42088     
42089     onResize: function(w, h){
42090         
42091         return;
42092         // not sure if this is needed..
42093         //this.combo.onResize(w,h);
42094         
42095         if(typeof w != 'number'){
42096             // we do not handle it!?!?
42097             return;
42098         }
42099         var tw = this.combo.trigger.getWidth();
42100         tw += this.addicon ? this.addicon.getWidth() : 0;
42101         tw += this.editicon ? this.editicon.getWidth() : 0;
42102         var x = w - tw;
42103         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42104             
42105         this.combo.trigger.setStyle('left', '0px');
42106         
42107         if(this.list && this.listWidth === undefined){
42108             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42109             this.list.setWidth(lw);
42110             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42111         }
42112         
42113     
42114         
42115     },
42116     
42117     addItem: function(rec)
42118     {
42119         var valueField = this.combo.valueField;
42120         var displayField = this.combo.displayField;
42121         if (this.items.indexOfKey(rec[valueField]) > -1) {
42122             //console.log("GOT " + rec.data.id);
42123             return;
42124         }
42125         
42126         var x = new Roo.form.ComboBoxArray.Item({
42127             //id : rec[this.idField],
42128             data : rec,
42129             displayField : displayField ,
42130             tipField : displayField ,
42131             cb : this
42132         });
42133         // use the 
42134         this.items.add(rec[valueField],x);
42135         // add it before the element..
42136         this.updateHiddenEl();
42137         x.render(this.outerWrap, this.wrap.dom);
42138         // add the image handler..
42139     },
42140     
42141     updateHiddenEl : function()
42142     {
42143         this.validate();
42144         if (!this.hiddenEl) {
42145             return;
42146         }
42147         var ar = [];
42148         var idField = this.combo.valueField;
42149         
42150         this.items.each(function(f) {
42151             ar.push(f.data[idField]);
42152            
42153         });
42154         this.hiddenEl.dom.value = ar.join(',');
42155         this.validate();
42156     },
42157     
42158     reset : function()
42159     {
42160         this.items.clear();
42161         
42162         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42163            el.remove();
42164         });
42165         
42166         this.el.dom.value = '';
42167         if (this.hiddenEl) {
42168             this.hiddenEl.dom.value = '';
42169         }
42170         
42171     },
42172     getValue: function()
42173     {
42174         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42175     },
42176     setValue: function(v) // not a valid action - must use addItems..
42177     {
42178          
42179         this.reset();
42180         
42181         
42182         
42183         if (this.store.isLocal && (typeof(v) == 'string')) {
42184             // then we can use the store to find the values..
42185             // comma seperated at present.. this needs to allow JSON based encoding..
42186             this.hiddenEl.value  = v;
42187             var v_ar = [];
42188             Roo.each(v.split(','), function(k) {
42189                 Roo.log("CHECK " + this.valueField + ',' + k);
42190                 var li = this.store.query(this.valueField, k);
42191                 if (!li.length) {
42192                     return;
42193                 }
42194                 var add = {};
42195                 add[this.valueField] = k;
42196                 add[this.displayField] = li.item(0).data[this.displayField];
42197                 
42198                 this.addItem(add);
42199             }, this) 
42200              
42201         }
42202         if (typeof(v) == 'object' ) {
42203             // then let's assume it's an array of objects..
42204             Roo.each(v, function(l) {
42205                 this.addItem(l);
42206             }, this);
42207              
42208         }
42209         
42210         
42211     },
42212     setFromData: function(v)
42213     {
42214         // this recieves an object, if setValues is called.
42215         this.reset();
42216         this.el.dom.value = v[this.displayField];
42217         this.hiddenEl.dom.value = v[this.valueField];
42218         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42219             return;
42220         }
42221         var kv = v[this.valueField];
42222         var dv = v[this.displayField];
42223         kv = typeof(kv) != 'string' ? '' : kv;
42224         dv = typeof(dv) != 'string' ? '' : dv;
42225         
42226         
42227         var keys = kv.split(',');
42228         var display = dv.split(',');
42229         for (var i = 0 ; i < keys.length; i++) {
42230             
42231             add = {};
42232             add[this.valueField] = keys[i];
42233             add[this.displayField] = display[i];
42234             this.addItem(add);
42235         }
42236       
42237         
42238     },
42239     
42240     /**
42241      * Validates the combox array value
42242      * @return {Boolean} True if the value is valid, else false
42243      */
42244     validate : function(){
42245         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42246             this.clearInvalid();
42247             return true;
42248         }
42249         return false;
42250     },
42251     
42252     validateValue : function(value){
42253         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42254         
42255     },
42256     
42257     /*@
42258      * overide
42259      * 
42260      */
42261     isDirty : function() {
42262         if(this.disabled) {
42263             return false;
42264         }
42265         
42266         try {
42267             var d = Roo.decode(String(this.originalValue));
42268         } catch (e) {
42269             return String(this.getValue()) !== String(this.originalValue);
42270         }
42271         
42272         var originalValue = [];
42273         
42274         for (var i = 0; i < d.length; i++){
42275             originalValue.push(d[i][this.valueField]);
42276         }
42277         
42278         return String(this.getValue()) !== String(originalValue.join(','));
42279         
42280     }
42281     
42282 });
42283
42284
42285
42286 /**
42287  * @class Roo.form.ComboBoxArray.Item
42288  * @extends Roo.BoxComponent
42289  * A selected item in the list
42290  *  Fred [x]  Brian [x]  [Pick another |v]
42291  * 
42292  * @constructor
42293  * Create a new item.
42294  * @param {Object} config Configuration options
42295  */
42296  
42297 Roo.form.ComboBoxArray.Item = function(config) {
42298     config.id = Roo.id();
42299     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42300 }
42301
42302 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42303     data : {},
42304     cb: false,
42305     displayField : false,
42306     tipField : false,
42307     
42308     
42309     defaultAutoCreate : {
42310         tag: 'div',
42311         cls: 'x-cbarray-item',
42312         cn : [ 
42313             { tag: 'div' },
42314             {
42315                 tag: 'img',
42316                 width:16,
42317                 height : 16,
42318                 src : Roo.BLANK_IMAGE_URL ,
42319                 align: 'center'
42320             }
42321         ]
42322         
42323     },
42324     
42325  
42326     onRender : function(ct, position)
42327     {
42328         Roo.form.Field.superclass.onRender.call(this, ct, position);
42329         
42330         if(!this.el){
42331             var cfg = this.getAutoCreate();
42332             this.el = ct.createChild(cfg, position);
42333         }
42334         
42335         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42336         
42337         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42338             this.cb.renderer(this.data) :
42339             String.format('{0}',this.data[this.displayField]);
42340         
42341             
42342         this.el.child('div').dom.setAttribute('qtip',
42343                         String.format('{0}',this.data[this.tipField])
42344         );
42345         
42346         this.el.child('img').on('click', this.remove, this);
42347         
42348     },
42349    
42350     remove : function()
42351     {
42352         if(this.cb.disabled){
42353             return;
42354         }
42355         
42356         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42357             this.cb.items.remove(this);
42358             this.el.child('img').un('click', this.remove, this);
42359             this.el.remove();
42360             this.cb.updateHiddenEl();
42361
42362             this.cb.fireEvent('remove', this.cb, this);
42363         }
42364         
42365     }
42366 });/*
42367  * Based on:
42368  * Ext JS Library 1.1.1
42369  * Copyright(c) 2006-2007, Ext JS, LLC.
42370  *
42371  * Originally Released Under LGPL - original licence link has changed is not relivant.
42372  *
42373  * Fork - LGPL
42374  * <script type="text/javascript">
42375  */
42376 /**
42377  * @class Roo.form.Checkbox
42378  * @extends Roo.form.Field
42379  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42380  * @constructor
42381  * Creates a new Checkbox
42382  * @param {Object} config Configuration options
42383  */
42384 Roo.form.Checkbox = function(config){
42385     Roo.form.Checkbox.superclass.constructor.call(this, config);
42386     this.addEvents({
42387         /**
42388          * @event check
42389          * Fires when the checkbox is checked or unchecked.
42390              * @param {Roo.form.Checkbox} this This checkbox
42391              * @param {Boolean} checked The new checked value
42392              */
42393         check : true
42394     });
42395 };
42396
42397 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42398     /**
42399      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42400      */
42401     focusClass : undefined,
42402     /**
42403      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42404      */
42405     fieldClass: "x-form-field",
42406     /**
42407      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42408      */
42409     checked: false,
42410     /**
42411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42412      * {tag: "input", type: "checkbox", autocomplete: "off"})
42413      */
42414     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42415     /**
42416      * @cfg {String} boxLabel The text that appears beside the checkbox
42417      */
42418     boxLabel : "",
42419     /**
42420      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42421      */  
42422     inputValue : '1',
42423     /**
42424      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42425      */
42426      valueOff: '0', // value when not checked..
42427
42428     actionMode : 'viewEl', 
42429     //
42430     // private
42431     itemCls : 'x-menu-check-item x-form-item',
42432     groupClass : 'x-menu-group-item',
42433     inputType : 'hidden',
42434     
42435     
42436     inSetChecked: false, // check that we are not calling self...
42437     
42438     inputElement: false, // real input element?
42439     basedOn: false, // ????
42440     
42441     isFormField: true, // not sure where this is needed!!!!
42442
42443     onResize : function(){
42444         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42445         if(!this.boxLabel){
42446             this.el.alignTo(this.wrap, 'c-c');
42447         }
42448     },
42449
42450     initEvents : function(){
42451         Roo.form.Checkbox.superclass.initEvents.call(this);
42452         this.el.on("click", this.onClick,  this);
42453         this.el.on("change", this.onClick,  this);
42454     },
42455
42456
42457     getResizeEl : function(){
42458         return this.wrap;
42459     },
42460
42461     getPositionEl : function(){
42462         return this.wrap;
42463     },
42464
42465     // private
42466     onRender : function(ct, position){
42467         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42468         /*
42469         if(this.inputValue !== undefined){
42470             this.el.dom.value = this.inputValue;
42471         }
42472         */
42473         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42474         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42475         var viewEl = this.wrap.createChild({ 
42476             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42477         this.viewEl = viewEl;   
42478         this.wrap.on('click', this.onClick,  this); 
42479         
42480         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42481         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42482         
42483         
42484         
42485         if(this.boxLabel){
42486             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42487         //    viewEl.on('click', this.onClick,  this); 
42488         }
42489         //if(this.checked){
42490             this.setChecked(this.checked);
42491         //}else{
42492             //this.checked = this.el.dom;
42493         //}
42494
42495     },
42496
42497     // private
42498     initValue : Roo.emptyFn,
42499
42500     /**
42501      * Returns the checked state of the checkbox.
42502      * @return {Boolean} True if checked, else false
42503      */
42504     getValue : function(){
42505         if(this.el){
42506             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42507         }
42508         return this.valueOff;
42509         
42510     },
42511
42512         // private
42513     onClick : function(){ 
42514         if (this.disabled) {
42515             return;
42516         }
42517         this.setChecked(!this.checked);
42518
42519         //if(this.el.dom.checked != this.checked){
42520         //    this.setValue(this.el.dom.checked);
42521        // }
42522     },
42523
42524     /**
42525      * Sets the checked state of the checkbox.
42526      * On is always based on a string comparison between inputValue and the param.
42527      * @param {Boolean/String} value - the value to set 
42528      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42529      */
42530     setValue : function(v,suppressEvent){
42531         
42532         
42533         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42534         //if(this.el && this.el.dom){
42535         //    this.el.dom.checked = this.checked;
42536         //    this.el.dom.defaultChecked = this.checked;
42537         //}
42538         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42539         //this.fireEvent("check", this, this.checked);
42540     },
42541     // private..
42542     setChecked : function(state,suppressEvent)
42543     {
42544         if (this.inSetChecked) {
42545             this.checked = state;
42546             return;
42547         }
42548         
42549     
42550         if(this.wrap){
42551             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42552         }
42553         this.checked = state;
42554         if(suppressEvent !== true){
42555             this.fireEvent('check', this, state);
42556         }
42557         this.inSetChecked = true;
42558         this.el.dom.value = state ? this.inputValue : this.valueOff;
42559         this.inSetChecked = false;
42560         
42561     },
42562     // handle setting of hidden value by some other method!!?!?
42563     setFromHidden: function()
42564     {
42565         if(!this.el){
42566             return;
42567         }
42568         //console.log("SET FROM HIDDEN");
42569         //alert('setFrom hidden');
42570         this.setValue(this.el.dom.value);
42571     },
42572     
42573     onDestroy : function()
42574     {
42575         if(this.viewEl){
42576             Roo.get(this.viewEl).remove();
42577         }
42578          
42579         Roo.form.Checkbox.superclass.onDestroy.call(this);
42580     },
42581     
42582     setBoxLabel : function(str)
42583     {
42584         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42585     }
42586
42587 });/*
42588  * Based on:
42589  * Ext JS Library 1.1.1
42590  * Copyright(c) 2006-2007, Ext JS, LLC.
42591  *
42592  * Originally Released Under LGPL - original licence link has changed is not relivant.
42593  *
42594  * Fork - LGPL
42595  * <script type="text/javascript">
42596  */
42597  
42598 /**
42599  * @class Roo.form.Radio
42600  * @extends Roo.form.Checkbox
42601  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42602  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42603  * @constructor
42604  * Creates a new Radio
42605  * @param {Object} config Configuration options
42606  */
42607 Roo.form.Radio = function(){
42608     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42609 };
42610 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42611     inputType: 'radio',
42612
42613     /**
42614      * If this radio is part of a group, it will return the selected value
42615      * @return {String}
42616      */
42617     getGroupValue : function(){
42618         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42619     },
42620     
42621     
42622     onRender : function(ct, position){
42623         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42624         
42625         if(this.inputValue !== undefined){
42626             this.el.dom.value = this.inputValue;
42627         }
42628          
42629         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42630         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42631         //var viewEl = this.wrap.createChild({ 
42632         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42633         //this.viewEl = viewEl;   
42634         //this.wrap.on('click', this.onClick,  this); 
42635         
42636         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42637         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42638         
42639         
42640         
42641         if(this.boxLabel){
42642             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42643         //    viewEl.on('click', this.onClick,  this); 
42644         }
42645          if(this.checked){
42646             this.el.dom.checked =   'checked' ;
42647         }
42648          
42649     } 
42650     
42651     
42652 });//<script type="text/javascript">
42653
42654 /*
42655  * Based  Ext JS Library 1.1.1
42656  * Copyright(c) 2006-2007, Ext JS, LLC.
42657  * LGPL
42658  *
42659  */
42660  
42661 /**
42662  * @class Roo.HtmlEditorCore
42663  * @extends Roo.Component
42664  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42665  *
42666  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42667  */
42668
42669 Roo.HtmlEditorCore = function(config){
42670     
42671     
42672     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42673     
42674     
42675     this.addEvents({
42676         /**
42677          * @event initialize
42678          * Fires when the editor is fully initialized (including the iframe)
42679          * @param {Roo.HtmlEditorCore} this
42680          */
42681         initialize: true,
42682         /**
42683          * @event activate
42684          * Fires when the editor is first receives the focus. Any insertion must wait
42685          * until after this event.
42686          * @param {Roo.HtmlEditorCore} this
42687          */
42688         activate: true,
42689          /**
42690          * @event beforesync
42691          * Fires before the textarea is updated with content from the editor iframe. Return false
42692          * to cancel the sync.
42693          * @param {Roo.HtmlEditorCore} this
42694          * @param {String} html
42695          */
42696         beforesync: true,
42697          /**
42698          * @event beforepush
42699          * Fires before the iframe editor is updated with content from the textarea. Return false
42700          * to cancel the push.
42701          * @param {Roo.HtmlEditorCore} this
42702          * @param {String} html
42703          */
42704         beforepush: true,
42705          /**
42706          * @event sync
42707          * Fires when the textarea is updated with content from the editor iframe.
42708          * @param {Roo.HtmlEditorCore} this
42709          * @param {String} html
42710          */
42711         sync: true,
42712          /**
42713          * @event push
42714          * Fires when the iframe editor is updated with content from the textarea.
42715          * @param {Roo.HtmlEditorCore} this
42716          * @param {String} html
42717          */
42718         push: true,
42719         
42720         /**
42721          * @event editorevent
42722          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42723          * @param {Roo.HtmlEditorCore} this
42724          */
42725         editorevent: true
42726         
42727     });
42728     
42729     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42730     
42731     // defaults : white / black...
42732     this.applyBlacklists();
42733     
42734     
42735     
42736 };
42737
42738
42739 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42740
42741
42742      /**
42743      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42744      */
42745     
42746     owner : false,
42747     
42748      /**
42749      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42750      *                        Roo.resizable.
42751      */
42752     resizable : false,
42753      /**
42754      * @cfg {Number} height (in pixels)
42755      */   
42756     height: 300,
42757    /**
42758      * @cfg {Number} width (in pixels)
42759      */   
42760     width: 500,
42761     
42762     /**
42763      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42764      * 
42765      */
42766     stylesheets: false,
42767     
42768     // id of frame..
42769     frameId: false,
42770     
42771     // private properties
42772     validationEvent : false,
42773     deferHeight: true,
42774     initialized : false,
42775     activated : false,
42776     sourceEditMode : false,
42777     onFocus : Roo.emptyFn,
42778     iframePad:3,
42779     hideMode:'offsets',
42780     
42781     clearUp: true,
42782     
42783     // blacklist + whitelisted elements..
42784     black: false,
42785     white: false,
42786      
42787     
42788
42789     /**
42790      * Protected method that will not generally be called directly. It
42791      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42792      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42793      */
42794     getDocMarkup : function(){
42795         // body styles..
42796         var st = '';
42797         
42798         // inherit styels from page...?? 
42799         if (this.stylesheets === false) {
42800             
42801             Roo.get(document.head).select('style').each(function(node) {
42802                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42803             });
42804             
42805             Roo.get(document.head).select('link').each(function(node) { 
42806                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42807             });
42808             
42809         } else if (!this.stylesheets.length) {
42810                 // simple..
42811                 st = '<style type="text/css">' +
42812                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42813                    '</style>';
42814         } else { 
42815             
42816         }
42817         
42818         st +=  '<style type="text/css">' +
42819             'IMG { cursor: pointer } ' +
42820         '</style>';
42821
42822         
42823         return '<html><head>' + st  +
42824             //<style type="text/css">' +
42825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42826             //'</style>' +
42827             ' </head><body class="roo-htmleditor-body"></body></html>';
42828     },
42829
42830     // private
42831     onRender : function(ct, position)
42832     {
42833         var _t = this;
42834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42836         
42837         
42838         this.el.dom.style.border = '0 none';
42839         this.el.dom.setAttribute('tabIndex', -1);
42840         this.el.addClass('x-hidden hide');
42841         
42842         
42843         
42844         if(Roo.isIE){ // fix IE 1px bogus margin
42845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42846         }
42847        
42848         
42849         this.frameId = Roo.id();
42850         
42851          
42852         
42853         var iframe = this.owner.wrap.createChild({
42854             tag: 'iframe',
42855             cls: 'form-control', // bootstrap..
42856             id: this.frameId,
42857             name: this.frameId,
42858             frameBorder : 'no',
42859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42860         }, this.el
42861         );
42862         
42863         
42864         this.iframe = iframe.dom;
42865
42866          this.assignDocWin();
42867         
42868         this.doc.designMode = 'on';
42869        
42870         this.doc.open();
42871         this.doc.write(this.getDocMarkup());
42872         this.doc.close();
42873
42874         
42875         var task = { // must defer to wait for browser to be ready
42876             run : function(){
42877                 //console.log("run task?" + this.doc.readyState);
42878                 this.assignDocWin();
42879                 if(this.doc.body || this.doc.readyState == 'complete'){
42880                     try {
42881                         this.doc.designMode="on";
42882                     } catch (e) {
42883                         return;
42884                     }
42885                     Roo.TaskMgr.stop(task);
42886                     this.initEditor.defer(10, this);
42887                 }
42888             },
42889             interval : 10,
42890             duration: 10000,
42891             scope: this
42892         };
42893         Roo.TaskMgr.start(task);
42894
42895     },
42896
42897     // private
42898     onResize : function(w, h)
42899     {
42900          Roo.log('resize: ' +w + ',' + h );
42901         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42902         if(!this.iframe){
42903             return;
42904         }
42905         if(typeof w == 'number'){
42906             
42907             this.iframe.style.width = w + 'px';
42908         }
42909         if(typeof h == 'number'){
42910             
42911             this.iframe.style.height = h + 'px';
42912             if(this.doc){
42913                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42914             }
42915         }
42916         
42917     },
42918
42919     /**
42920      * Toggles the editor between standard and source edit mode.
42921      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42922      */
42923     toggleSourceEdit : function(sourceEditMode){
42924         
42925         this.sourceEditMode = sourceEditMode === true;
42926         
42927         if(this.sourceEditMode){
42928  
42929             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42930             
42931         }else{
42932             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42933             //this.iframe.className = '';
42934             this.deferFocus();
42935         }
42936         //this.setSize(this.owner.wrap.getSize());
42937         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42938     },
42939
42940     
42941   
42942
42943     /**
42944      * Protected method that will not generally be called directly. If you need/want
42945      * custom HTML cleanup, this is the method you should override.
42946      * @param {String} html The HTML to be cleaned
42947      * return {String} The cleaned HTML
42948      */
42949     cleanHtml : function(html){
42950         html = String(html);
42951         if(html.length > 5){
42952             if(Roo.isSafari){ // strip safari nonsense
42953                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42954             }
42955         }
42956         if(html == '&nbsp;'){
42957             html = '';
42958         }
42959         return html;
42960     },
42961
42962     /**
42963      * HTML Editor -> Textarea
42964      * Protected method that will not generally be called directly. Syncs the contents
42965      * of the editor iframe with the textarea.
42966      */
42967     syncValue : function(){
42968         if(this.initialized){
42969             var bd = (this.doc.body || this.doc.documentElement);
42970             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42971             var html = bd.innerHTML;
42972             if(Roo.isSafari){
42973                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42974                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42975                 if(m && m[1]){
42976                     html = '<div style="'+m[0]+'">' + html + '</div>';
42977                 }
42978             }
42979             html = this.cleanHtml(html);
42980             // fix up the special chars.. normaly like back quotes in word...
42981             // however we do not want to do this with chinese..
42982             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42983                 var cc = b.charCodeAt();
42984                 if (
42985                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42986                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42987                     (cc >= 0xf900 && cc < 0xfb00 )
42988                 ) {
42989                         return b;
42990                 }
42991                 return "&#"+cc+";" 
42992             });
42993             if(this.owner.fireEvent('beforesync', this, html) !== false){
42994                 this.el.dom.value = html;
42995                 this.owner.fireEvent('sync', this, html);
42996             }
42997         }
42998     },
42999
43000     /**
43001      * Protected method that will not generally be called directly. Pushes the value of the textarea
43002      * into the iframe editor.
43003      */
43004     pushValue : function(){
43005         if(this.initialized){
43006             var v = this.el.dom.value.trim();
43007             
43008 //            if(v.length < 1){
43009 //                v = '&#160;';
43010 //            }
43011             
43012             if(this.owner.fireEvent('beforepush', this, v) !== false){
43013                 var d = (this.doc.body || this.doc.documentElement);
43014                 d.innerHTML = v;
43015                 this.cleanUpPaste();
43016                 this.el.dom.value = d.innerHTML;
43017                 this.owner.fireEvent('push', this, v);
43018             }
43019         }
43020     },
43021
43022     // private
43023     deferFocus : function(){
43024         this.focus.defer(10, this);
43025     },
43026
43027     // doc'ed in Field
43028     focus : function(){
43029         if(this.win && !this.sourceEditMode){
43030             this.win.focus();
43031         }else{
43032             this.el.focus();
43033         }
43034     },
43035     
43036     assignDocWin: function()
43037     {
43038         var iframe = this.iframe;
43039         
43040          if(Roo.isIE){
43041             this.doc = iframe.contentWindow.document;
43042             this.win = iframe.contentWindow;
43043         } else {
43044 //            if (!Roo.get(this.frameId)) {
43045 //                return;
43046 //            }
43047 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43048 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43049             
43050             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43051                 return;
43052             }
43053             
43054             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43055             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43056         }
43057     },
43058     
43059     // private
43060     initEditor : function(){
43061         //console.log("INIT EDITOR");
43062         this.assignDocWin();
43063         
43064         
43065         
43066         this.doc.designMode="on";
43067         this.doc.open();
43068         this.doc.write(this.getDocMarkup());
43069         this.doc.close();
43070         
43071         var dbody = (this.doc.body || this.doc.documentElement);
43072         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43073         // this copies styles from the containing element into thsi one..
43074         // not sure why we need all of this..
43075         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43076         
43077         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43078         //ss['background-attachment'] = 'fixed'; // w3c
43079         dbody.bgProperties = 'fixed'; // ie
43080         //Roo.DomHelper.applyStyles(dbody, ss);
43081         Roo.EventManager.on(this.doc, {
43082             //'mousedown': this.onEditorEvent,
43083             'mouseup': this.onEditorEvent,
43084             'dblclick': this.onEditorEvent,
43085             'click': this.onEditorEvent,
43086             'keyup': this.onEditorEvent,
43087             buffer:100,
43088             scope: this
43089         });
43090         if(Roo.isGecko){
43091             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43092         }
43093         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43094             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43095         }
43096         this.initialized = true;
43097
43098         this.owner.fireEvent('initialize', this);
43099         this.pushValue();
43100     },
43101
43102     // private
43103     onDestroy : function(){
43104         
43105         
43106         
43107         if(this.rendered){
43108             
43109             //for (var i =0; i < this.toolbars.length;i++) {
43110             //    // fixme - ask toolbars for heights?
43111             //    this.toolbars[i].onDestroy();
43112            // }
43113             
43114             //this.wrap.dom.innerHTML = '';
43115             //this.wrap.remove();
43116         }
43117     },
43118
43119     // private
43120     onFirstFocus : function(){
43121         
43122         this.assignDocWin();
43123         
43124         
43125         this.activated = true;
43126          
43127     
43128         if(Roo.isGecko){ // prevent silly gecko errors
43129             this.win.focus();
43130             var s = this.win.getSelection();
43131             if(!s.focusNode || s.focusNode.nodeType != 3){
43132                 var r = s.getRangeAt(0);
43133                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43134                 r.collapse(true);
43135                 this.deferFocus();
43136             }
43137             try{
43138                 this.execCmd('useCSS', true);
43139                 this.execCmd('styleWithCSS', false);
43140             }catch(e){}
43141         }
43142         this.owner.fireEvent('activate', this);
43143     },
43144
43145     // private
43146     adjustFont: function(btn){
43147         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43148         //if(Roo.isSafari){ // safari
43149         //    adjust *= 2;
43150        // }
43151         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43152         if(Roo.isSafari){ // safari
43153             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43154             v =  (v < 10) ? 10 : v;
43155             v =  (v > 48) ? 48 : v;
43156             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43157             
43158         }
43159         
43160         
43161         v = Math.max(1, v+adjust);
43162         
43163         this.execCmd('FontSize', v  );
43164     },
43165
43166     onEditorEvent : function(e)
43167     {
43168         this.owner.fireEvent('editorevent', this, e);
43169       //  this.updateToolbar();
43170         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43171     },
43172
43173     insertTag : function(tg)
43174     {
43175         // could be a bit smarter... -> wrap the current selected tRoo..
43176         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43177             
43178             range = this.createRange(this.getSelection());
43179             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43180             wrappingNode.appendChild(range.extractContents());
43181             range.insertNode(wrappingNode);
43182
43183             return;
43184             
43185             
43186             
43187         }
43188         this.execCmd("formatblock",   tg);
43189         
43190     },
43191     
43192     insertText : function(txt)
43193     {
43194         
43195         
43196         var range = this.createRange();
43197         range.deleteContents();
43198                //alert(Sender.getAttribute('label'));
43199                
43200         range.insertNode(this.doc.createTextNode(txt));
43201     } ,
43202     
43203      
43204
43205     /**
43206      * Executes a Midas editor command on the editor document and performs necessary focus and
43207      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43208      * @param {String} cmd The Midas command
43209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43210      */
43211     relayCmd : function(cmd, value){
43212         this.win.focus();
43213         this.execCmd(cmd, value);
43214         this.owner.fireEvent('editorevent', this);
43215         //this.updateToolbar();
43216         this.owner.deferFocus();
43217     },
43218
43219     /**
43220      * Executes a Midas editor command directly on the editor document.
43221      * For visual commands, you should use {@link #relayCmd} instead.
43222      * <b>This should only be called after the editor is initialized.</b>
43223      * @param {String} cmd The Midas command
43224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43225      */
43226     execCmd : function(cmd, value){
43227         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43228         this.syncValue();
43229     },
43230  
43231  
43232    
43233     /**
43234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43235      * to insert tRoo.
43236      * @param {String} text | dom node.. 
43237      */
43238     insertAtCursor : function(text)
43239     {
43240         
43241         
43242         
43243         if(!this.activated){
43244             return;
43245         }
43246         /*
43247         if(Roo.isIE){
43248             this.win.focus();
43249             var r = this.doc.selection.createRange();
43250             if(r){
43251                 r.collapse(true);
43252                 r.pasteHTML(text);
43253                 this.syncValue();
43254                 this.deferFocus();
43255             
43256             }
43257             return;
43258         }
43259         */
43260         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43261             this.win.focus();
43262             
43263             
43264             // from jquery ui (MIT licenced)
43265             var range, node;
43266             var win = this.win;
43267             
43268             if (win.getSelection && win.getSelection().getRangeAt) {
43269                 range = win.getSelection().getRangeAt(0);
43270                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43271                 range.insertNode(node);
43272             } else if (win.document.selection && win.document.selection.createRange) {
43273                 // no firefox support
43274                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43275                 win.document.selection.createRange().pasteHTML(txt);
43276             } else {
43277                 // no firefox support
43278                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43279                 this.execCmd('InsertHTML', txt);
43280             } 
43281             
43282             this.syncValue();
43283             
43284             this.deferFocus();
43285         }
43286     },
43287  // private
43288     mozKeyPress : function(e){
43289         if(e.ctrlKey){
43290             var c = e.getCharCode(), cmd;
43291           
43292             if(c > 0){
43293                 c = String.fromCharCode(c).toLowerCase();
43294                 switch(c){
43295                     case 'b':
43296                         cmd = 'bold';
43297                         break;
43298                     case 'i':
43299                         cmd = 'italic';
43300                         break;
43301                     
43302                     case 'u':
43303                         cmd = 'underline';
43304                         break;
43305                     
43306                     case 'v':
43307                         this.cleanUpPaste.defer(100, this);
43308                         return;
43309                         
43310                 }
43311                 if(cmd){
43312                     this.win.focus();
43313                     this.execCmd(cmd);
43314                     this.deferFocus();
43315                     e.preventDefault();
43316                 }
43317                 
43318             }
43319         }
43320     },
43321
43322     // private
43323     fixKeys : function(){ // load time branching for fastest keydown performance
43324         if(Roo.isIE){
43325             return function(e){
43326                 var k = e.getKey(), r;
43327                 if(k == e.TAB){
43328                     e.stopEvent();
43329                     r = this.doc.selection.createRange();
43330                     if(r){
43331                         r.collapse(true);
43332                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43333                         this.deferFocus();
43334                     }
43335                     return;
43336                 }
43337                 
43338                 if(k == e.ENTER){
43339                     r = this.doc.selection.createRange();
43340                     if(r){
43341                         var target = r.parentElement();
43342                         if(!target || target.tagName.toLowerCase() != 'li'){
43343                             e.stopEvent();
43344                             r.pasteHTML('<br />');
43345                             r.collapse(false);
43346                             r.select();
43347                         }
43348                     }
43349                 }
43350                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43351                     this.cleanUpPaste.defer(100, this);
43352                     return;
43353                 }
43354                 
43355                 
43356             };
43357         }else if(Roo.isOpera){
43358             return function(e){
43359                 var k = e.getKey();
43360                 if(k == e.TAB){
43361                     e.stopEvent();
43362                     this.win.focus();
43363                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43364                     this.deferFocus();
43365                 }
43366                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43367                     this.cleanUpPaste.defer(100, this);
43368                     return;
43369                 }
43370                 
43371             };
43372         }else if(Roo.isSafari){
43373             return function(e){
43374                 var k = e.getKey();
43375                 
43376                 if(k == e.TAB){
43377                     e.stopEvent();
43378                     this.execCmd('InsertText','\t');
43379                     this.deferFocus();
43380                     return;
43381                 }
43382                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43383                     this.cleanUpPaste.defer(100, this);
43384                     return;
43385                 }
43386                 
43387              };
43388         }
43389     }(),
43390     
43391     getAllAncestors: function()
43392     {
43393         var p = this.getSelectedNode();
43394         var a = [];
43395         if (!p) {
43396             a.push(p); // push blank onto stack..
43397             p = this.getParentElement();
43398         }
43399         
43400         
43401         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43402             a.push(p);
43403             p = p.parentNode;
43404         }
43405         a.push(this.doc.body);
43406         return a;
43407     },
43408     lastSel : false,
43409     lastSelNode : false,
43410     
43411     
43412     getSelection : function() 
43413     {
43414         this.assignDocWin();
43415         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43416     },
43417     
43418     getSelectedNode: function() 
43419     {
43420         // this may only work on Gecko!!!
43421         
43422         // should we cache this!!!!
43423         
43424         
43425         
43426          
43427         var range = this.createRange(this.getSelection()).cloneRange();
43428         
43429         if (Roo.isIE) {
43430             var parent = range.parentElement();
43431             while (true) {
43432                 var testRange = range.duplicate();
43433                 testRange.moveToElementText(parent);
43434                 if (testRange.inRange(range)) {
43435                     break;
43436                 }
43437                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43438                     break;
43439                 }
43440                 parent = parent.parentElement;
43441             }
43442             return parent;
43443         }
43444         
43445         // is ancestor a text element.
43446         var ac =  range.commonAncestorContainer;
43447         if (ac.nodeType == 3) {
43448             ac = ac.parentNode;
43449         }
43450         
43451         var ar = ac.childNodes;
43452          
43453         var nodes = [];
43454         var other_nodes = [];
43455         var has_other_nodes = false;
43456         for (var i=0;i<ar.length;i++) {
43457             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43458                 continue;
43459             }
43460             // fullly contained node.
43461             
43462             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43463                 nodes.push(ar[i]);
43464                 continue;
43465             }
43466             
43467             // probably selected..
43468             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43469                 other_nodes.push(ar[i]);
43470                 continue;
43471             }
43472             // outer..
43473             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43474                 continue;
43475             }
43476             
43477             
43478             has_other_nodes = true;
43479         }
43480         if (!nodes.length && other_nodes.length) {
43481             nodes= other_nodes;
43482         }
43483         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43484             return false;
43485         }
43486         
43487         return nodes[0];
43488     },
43489     createRange: function(sel)
43490     {
43491         // this has strange effects when using with 
43492         // top toolbar - not sure if it's a great idea.
43493         //this.editor.contentWindow.focus();
43494         if (typeof sel != "undefined") {
43495             try {
43496                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43497             } catch(e) {
43498                 return this.doc.createRange();
43499             }
43500         } else {
43501             return this.doc.createRange();
43502         }
43503     },
43504     getParentElement: function()
43505     {
43506         
43507         this.assignDocWin();
43508         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43509         
43510         var range = this.createRange(sel);
43511          
43512         try {
43513             var p = range.commonAncestorContainer;
43514             while (p.nodeType == 3) { // text node
43515                 p = p.parentNode;
43516             }
43517             return p;
43518         } catch (e) {
43519             return null;
43520         }
43521     
43522     },
43523     /***
43524      *
43525      * Range intersection.. the hard stuff...
43526      *  '-1' = before
43527      *  '0' = hits..
43528      *  '1' = after.
43529      *         [ -- selected range --- ]
43530      *   [fail]                        [fail]
43531      *
43532      *    basically..
43533      *      if end is before start or  hits it. fail.
43534      *      if start is after end or hits it fail.
43535      *
43536      *   if either hits (but other is outside. - then it's not 
43537      *   
43538      *    
43539      **/
43540     
43541     
43542     // @see http://www.thismuchiknow.co.uk/?p=64.
43543     rangeIntersectsNode : function(range, node)
43544     {
43545         var nodeRange = node.ownerDocument.createRange();
43546         try {
43547             nodeRange.selectNode(node);
43548         } catch (e) {
43549             nodeRange.selectNodeContents(node);
43550         }
43551     
43552         var rangeStartRange = range.cloneRange();
43553         rangeStartRange.collapse(true);
43554     
43555         var rangeEndRange = range.cloneRange();
43556         rangeEndRange.collapse(false);
43557     
43558         var nodeStartRange = nodeRange.cloneRange();
43559         nodeStartRange.collapse(true);
43560     
43561         var nodeEndRange = nodeRange.cloneRange();
43562         nodeEndRange.collapse(false);
43563     
43564         return rangeStartRange.compareBoundaryPoints(
43565                  Range.START_TO_START, nodeEndRange) == -1 &&
43566                rangeEndRange.compareBoundaryPoints(
43567                  Range.START_TO_START, nodeStartRange) == 1;
43568         
43569          
43570     },
43571     rangeCompareNode : function(range, node)
43572     {
43573         var nodeRange = node.ownerDocument.createRange();
43574         try {
43575             nodeRange.selectNode(node);
43576         } catch (e) {
43577             nodeRange.selectNodeContents(node);
43578         }
43579         
43580         
43581         range.collapse(true);
43582     
43583         nodeRange.collapse(true);
43584      
43585         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43586         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43587          
43588         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43589         
43590         var nodeIsBefore   =  ss == 1;
43591         var nodeIsAfter    = ee == -1;
43592         
43593         if (nodeIsBefore && nodeIsAfter) {
43594             return 0; // outer
43595         }
43596         if (!nodeIsBefore && nodeIsAfter) {
43597             return 1; //right trailed.
43598         }
43599         
43600         if (nodeIsBefore && !nodeIsAfter) {
43601             return 2;  // left trailed.
43602         }
43603         // fully contined.
43604         return 3;
43605     },
43606
43607     // private? - in a new class?
43608     cleanUpPaste :  function()
43609     {
43610         // cleans up the whole document..
43611         Roo.log('cleanuppaste');
43612         
43613         this.cleanUpChildren(this.doc.body);
43614         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43615         if (clean != this.doc.body.innerHTML) {
43616             this.doc.body.innerHTML = clean;
43617         }
43618         
43619     },
43620     
43621     cleanWordChars : function(input) {// change the chars to hex code
43622         var he = Roo.HtmlEditorCore;
43623         
43624         var output = input;
43625         Roo.each(he.swapCodes, function(sw) { 
43626             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43627             
43628             output = output.replace(swapper, sw[1]);
43629         });
43630         
43631         return output;
43632     },
43633     
43634     
43635     cleanUpChildren : function (n)
43636     {
43637         if (!n.childNodes.length) {
43638             return;
43639         }
43640         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43641            this.cleanUpChild(n.childNodes[i]);
43642         }
43643     },
43644     
43645     
43646         
43647     
43648     cleanUpChild : function (node)
43649     {
43650         var ed = this;
43651         //console.log(node);
43652         if (node.nodeName == "#text") {
43653             // clean up silly Windows -- stuff?
43654             return; 
43655         }
43656         if (node.nodeName == "#comment") {
43657             node.parentNode.removeChild(node);
43658             // clean up silly Windows -- stuff?
43659             return; 
43660         }
43661         var lcname = node.tagName.toLowerCase();
43662         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43663         // whitelist of tags..
43664         
43665         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43666             // remove node.
43667             node.parentNode.removeChild(node);
43668             return;
43669             
43670         }
43671         
43672         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43673         
43674         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43675         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43676         
43677         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43678         //    remove_keep_children = true;
43679         //}
43680         
43681         if (remove_keep_children) {
43682             this.cleanUpChildren(node);
43683             // inserts everything just before this node...
43684             while (node.childNodes.length) {
43685                 var cn = node.childNodes[0];
43686                 node.removeChild(cn);
43687                 node.parentNode.insertBefore(cn, node);
43688             }
43689             node.parentNode.removeChild(node);
43690             return;
43691         }
43692         
43693         if (!node.attributes || !node.attributes.length) {
43694             this.cleanUpChildren(node);
43695             return;
43696         }
43697         
43698         function cleanAttr(n,v)
43699         {
43700             
43701             if (v.match(/^\./) || v.match(/^\//)) {
43702                 return;
43703             }
43704             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43705                 return;
43706             }
43707             if (v.match(/^#/)) {
43708                 return;
43709             }
43710 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43711             node.removeAttribute(n);
43712             
43713         }
43714         
43715         var cwhite = this.cwhite;
43716         var cblack = this.cblack;
43717             
43718         function cleanStyle(n,v)
43719         {
43720             if (v.match(/expression/)) { //XSS?? should we even bother..
43721                 node.removeAttribute(n);
43722                 return;
43723             }
43724             
43725             var parts = v.split(/;/);
43726             var clean = [];
43727             
43728             Roo.each(parts, function(p) {
43729                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43730                 if (!p.length) {
43731                     return true;
43732                 }
43733                 var l = p.split(':').shift().replace(/\s+/g,'');
43734                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43735                 
43736                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43737 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43738                     //node.removeAttribute(n);
43739                     return true;
43740                 }
43741                 //Roo.log()
43742                 // only allow 'c whitelisted system attributes'
43743                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43744 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43745                     //node.removeAttribute(n);
43746                     return true;
43747                 }
43748                 
43749                 
43750                  
43751                 
43752                 clean.push(p);
43753                 return true;
43754             });
43755             if (clean.length) { 
43756                 node.setAttribute(n, clean.join(';'));
43757             } else {
43758                 node.removeAttribute(n);
43759             }
43760             
43761         }
43762         
43763         
43764         for (var i = node.attributes.length-1; i > -1 ; i--) {
43765             var a = node.attributes[i];
43766             //console.log(a);
43767             
43768             if (a.name.toLowerCase().substr(0,2)=='on')  {
43769                 node.removeAttribute(a.name);
43770                 continue;
43771             }
43772             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43773                 node.removeAttribute(a.name);
43774                 continue;
43775             }
43776             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43777                 cleanAttr(a.name,a.value); // fixme..
43778                 continue;
43779             }
43780             if (a.name == 'style') {
43781                 cleanStyle(a.name,a.value);
43782                 continue;
43783             }
43784             /// clean up MS crap..
43785             // tecnically this should be a list of valid class'es..
43786             
43787             
43788             if (a.name == 'class') {
43789                 if (a.value.match(/^Mso/)) {
43790                     node.className = '';
43791                 }
43792                 
43793                 if (a.value.match(/body/)) {
43794                     node.className = '';
43795                 }
43796                 continue;
43797             }
43798             
43799             // style cleanup!?
43800             // class cleanup?
43801             
43802         }
43803         
43804         
43805         this.cleanUpChildren(node);
43806         
43807         
43808     },
43809     
43810     /**
43811      * Clean up MS wordisms...
43812      */
43813     cleanWord : function(node)
43814     {
43815         
43816         
43817         if (!node) {
43818             this.cleanWord(this.doc.body);
43819             return;
43820         }
43821         if (node.nodeName == "#text") {
43822             // clean up silly Windows -- stuff?
43823             return; 
43824         }
43825         if (node.nodeName == "#comment") {
43826             node.parentNode.removeChild(node);
43827             // clean up silly Windows -- stuff?
43828             return; 
43829         }
43830         
43831         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43832             node.parentNode.removeChild(node);
43833             return;
43834         }
43835         
43836         // remove - but keep children..
43837         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43838             while (node.childNodes.length) {
43839                 var cn = node.childNodes[0];
43840                 node.removeChild(cn);
43841                 node.parentNode.insertBefore(cn, node);
43842             }
43843             node.parentNode.removeChild(node);
43844             this.iterateChildren(node, this.cleanWord);
43845             return;
43846         }
43847         // clean styles
43848         if (node.className.length) {
43849             
43850             var cn = node.className.split(/\W+/);
43851             var cna = [];
43852             Roo.each(cn, function(cls) {
43853                 if (cls.match(/Mso[a-zA-Z]+/)) {
43854                     return;
43855                 }
43856                 cna.push(cls);
43857             });
43858             node.className = cna.length ? cna.join(' ') : '';
43859             if (!cna.length) {
43860                 node.removeAttribute("class");
43861             }
43862         }
43863         
43864         if (node.hasAttribute("lang")) {
43865             node.removeAttribute("lang");
43866         }
43867         
43868         if (node.hasAttribute("style")) {
43869             
43870             var styles = node.getAttribute("style").split(";");
43871             var nstyle = [];
43872             Roo.each(styles, function(s) {
43873                 if (!s.match(/:/)) {
43874                     return;
43875                 }
43876                 var kv = s.split(":");
43877                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43878                     return;
43879                 }
43880                 // what ever is left... we allow.
43881                 nstyle.push(s);
43882             });
43883             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43884             if (!nstyle.length) {
43885                 node.removeAttribute('style');
43886             }
43887         }
43888         this.iterateChildren(node, this.cleanWord);
43889         
43890         
43891         
43892     },
43893     /**
43894      * iterateChildren of a Node, calling fn each time, using this as the scole..
43895      * @param {DomNode} node node to iterate children of.
43896      * @param {Function} fn method of this class to call on each item.
43897      */
43898     iterateChildren : function(node, fn)
43899     {
43900         if (!node.childNodes.length) {
43901                 return;
43902         }
43903         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43904            fn.call(this, node.childNodes[i])
43905         }
43906     },
43907     
43908     
43909     /**
43910      * cleanTableWidths.
43911      *
43912      * Quite often pasting from word etc.. results in tables with column and widths.
43913      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43914      *
43915      */
43916     cleanTableWidths : function(node)
43917     {
43918          
43919          
43920         if (!node) {
43921             this.cleanTableWidths(this.doc.body);
43922             return;
43923         }
43924         
43925         // ignore list...
43926         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43927             return; 
43928         }
43929         Roo.log(node.tagName);
43930         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43931             this.iterateChildren(node, this.cleanTableWidths);
43932             return;
43933         }
43934         if (node.hasAttribute('width')) {
43935             node.removeAttribute('width');
43936         }
43937         
43938          
43939         if (node.hasAttribute("style")) {
43940             // pretty basic...
43941             
43942             var styles = node.getAttribute("style").split(";");
43943             var nstyle = [];
43944             Roo.each(styles, function(s) {
43945                 if (!s.match(/:/)) {
43946                     return;
43947                 }
43948                 var kv = s.split(":");
43949                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43950                     return;
43951                 }
43952                 // what ever is left... we allow.
43953                 nstyle.push(s);
43954             });
43955             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43956             if (!nstyle.length) {
43957                 node.removeAttribute('style');
43958             }
43959         }
43960         
43961         this.iterateChildren(node, this.cleanTableWidths);
43962         
43963         
43964     },
43965     
43966     
43967     
43968     
43969     domToHTML : function(currentElement, depth, nopadtext) {
43970         
43971         depth = depth || 0;
43972         nopadtext = nopadtext || false;
43973     
43974         if (!currentElement) {
43975             return this.domToHTML(this.doc.body);
43976         }
43977         
43978         //Roo.log(currentElement);
43979         var j;
43980         var allText = false;
43981         var nodeName = currentElement.nodeName;
43982         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43983         
43984         if  (nodeName == '#text') {
43985             
43986             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43987         }
43988         
43989         
43990         var ret = '';
43991         if (nodeName != 'BODY') {
43992              
43993             var i = 0;
43994             // Prints the node tagName, such as <A>, <IMG>, etc
43995             if (tagName) {
43996                 var attr = [];
43997                 for(i = 0; i < currentElement.attributes.length;i++) {
43998                     // quoting?
43999                     var aname = currentElement.attributes.item(i).name;
44000                     if (!currentElement.attributes.item(i).value.length) {
44001                         continue;
44002                     }
44003                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44004                 }
44005                 
44006                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44007             } 
44008             else {
44009                 
44010                 // eack
44011             }
44012         } else {
44013             tagName = false;
44014         }
44015         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44016             return ret;
44017         }
44018         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44019             nopadtext = true;
44020         }
44021         
44022         
44023         // Traverse the tree
44024         i = 0;
44025         var currentElementChild = currentElement.childNodes.item(i);
44026         var allText = true;
44027         var innerHTML  = '';
44028         lastnode = '';
44029         while (currentElementChild) {
44030             // Formatting code (indent the tree so it looks nice on the screen)
44031             var nopad = nopadtext;
44032             if (lastnode == 'SPAN') {
44033                 nopad  = true;
44034             }
44035             // text
44036             if  (currentElementChild.nodeName == '#text') {
44037                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44038                 toadd = nopadtext ? toadd : toadd.trim();
44039                 if (!nopad && toadd.length > 80) {
44040                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44041                 }
44042                 innerHTML  += toadd;
44043                 
44044                 i++;
44045                 currentElementChild = currentElement.childNodes.item(i);
44046                 lastNode = '';
44047                 continue;
44048             }
44049             allText = false;
44050             
44051             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44052                 
44053             // Recursively traverse the tree structure of the child node
44054             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44055             lastnode = currentElementChild.nodeName;
44056             i++;
44057             currentElementChild=currentElement.childNodes.item(i);
44058         }
44059         
44060         ret += innerHTML;
44061         
44062         if (!allText) {
44063                 // The remaining code is mostly for formatting the tree
44064             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44065         }
44066         
44067         
44068         if (tagName) {
44069             ret+= "</"+tagName+">";
44070         }
44071         return ret;
44072         
44073     },
44074         
44075     applyBlacklists : function()
44076     {
44077         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44078         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44079         
44080         this.white = [];
44081         this.black = [];
44082         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44083             if (b.indexOf(tag) > -1) {
44084                 return;
44085             }
44086             this.white.push(tag);
44087             
44088         }, this);
44089         
44090         Roo.each(w, function(tag) {
44091             if (b.indexOf(tag) > -1) {
44092                 return;
44093             }
44094             if (this.white.indexOf(tag) > -1) {
44095                 return;
44096             }
44097             this.white.push(tag);
44098             
44099         }, this);
44100         
44101         
44102         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44103             if (w.indexOf(tag) > -1) {
44104                 return;
44105             }
44106             this.black.push(tag);
44107             
44108         }, this);
44109         
44110         Roo.each(b, function(tag) {
44111             if (w.indexOf(tag) > -1) {
44112                 return;
44113             }
44114             if (this.black.indexOf(tag) > -1) {
44115                 return;
44116             }
44117             this.black.push(tag);
44118             
44119         }, this);
44120         
44121         
44122         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44123         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44124         
44125         this.cwhite = [];
44126         this.cblack = [];
44127         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44128             if (b.indexOf(tag) > -1) {
44129                 return;
44130             }
44131             this.cwhite.push(tag);
44132             
44133         }, this);
44134         
44135         Roo.each(w, function(tag) {
44136             if (b.indexOf(tag) > -1) {
44137                 return;
44138             }
44139             if (this.cwhite.indexOf(tag) > -1) {
44140                 return;
44141             }
44142             this.cwhite.push(tag);
44143             
44144         }, this);
44145         
44146         
44147         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44148             if (w.indexOf(tag) > -1) {
44149                 return;
44150             }
44151             this.cblack.push(tag);
44152             
44153         }, this);
44154         
44155         Roo.each(b, function(tag) {
44156             if (w.indexOf(tag) > -1) {
44157                 return;
44158             }
44159             if (this.cblack.indexOf(tag) > -1) {
44160                 return;
44161             }
44162             this.cblack.push(tag);
44163             
44164         }, this);
44165     },
44166     
44167     setStylesheets : function(stylesheets)
44168     {
44169         if(typeof(stylesheets) == 'string'){
44170             Roo.get(this.iframe.contentDocument.head).createChild({
44171                 tag : 'link',
44172                 rel : 'stylesheet',
44173                 type : 'text/css',
44174                 href : stylesheets
44175             });
44176             
44177             return;
44178         }
44179         var _this = this;
44180      
44181         Roo.each(stylesheets, function(s) {
44182             if(!s.length){
44183                 return;
44184             }
44185             
44186             Roo.get(_this.iframe.contentDocument.head).createChild({
44187                 tag : 'link',
44188                 rel : 'stylesheet',
44189                 type : 'text/css',
44190                 href : s
44191             });
44192         });
44193
44194         
44195     },
44196     
44197     removeStylesheets : function()
44198     {
44199         var _this = this;
44200         
44201         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44202             s.remove();
44203         });
44204     }
44205     
44206     // hide stuff that is not compatible
44207     /**
44208      * @event blur
44209      * @hide
44210      */
44211     /**
44212      * @event change
44213      * @hide
44214      */
44215     /**
44216      * @event focus
44217      * @hide
44218      */
44219     /**
44220      * @event specialkey
44221      * @hide
44222      */
44223     /**
44224      * @cfg {String} fieldClass @hide
44225      */
44226     /**
44227      * @cfg {String} focusClass @hide
44228      */
44229     /**
44230      * @cfg {String} autoCreate @hide
44231      */
44232     /**
44233      * @cfg {String} inputType @hide
44234      */
44235     /**
44236      * @cfg {String} invalidClass @hide
44237      */
44238     /**
44239      * @cfg {String} invalidText @hide
44240      */
44241     /**
44242      * @cfg {String} msgFx @hide
44243      */
44244     /**
44245      * @cfg {String} validateOnBlur @hide
44246      */
44247 });
44248
44249 Roo.HtmlEditorCore.white = [
44250         'area', 'br', 'img', 'input', 'hr', 'wbr',
44251         
44252        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44253        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44254        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44255        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44256        'table',   'ul',         'xmp', 
44257        
44258        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44259       'thead',   'tr', 
44260      
44261       'dir', 'menu', 'ol', 'ul', 'dl',
44262        
44263       'embed',  'object'
44264 ];
44265
44266
44267 Roo.HtmlEditorCore.black = [
44268     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44269         'applet', // 
44270         'base',   'basefont', 'bgsound', 'blink',  'body', 
44271         'frame',  'frameset', 'head',    'html',   'ilayer', 
44272         'iframe', 'layer',  'link',     'meta',    'object',   
44273         'script', 'style' ,'title',  'xml' // clean later..
44274 ];
44275 Roo.HtmlEditorCore.clean = [
44276     'script', 'style', 'title', 'xml'
44277 ];
44278 Roo.HtmlEditorCore.remove = [
44279     'font'
44280 ];
44281 // attributes..
44282
44283 Roo.HtmlEditorCore.ablack = [
44284     'on'
44285 ];
44286     
44287 Roo.HtmlEditorCore.aclean = [ 
44288     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44289 ];
44290
44291 // protocols..
44292 Roo.HtmlEditorCore.pwhite= [
44293         'http',  'https',  'mailto'
44294 ];
44295
44296 // white listed style attributes.
44297 Roo.HtmlEditorCore.cwhite= [
44298       //  'text-align', /// default is to allow most things..
44299       
44300          
44301 //        'font-size'//??
44302 ];
44303
44304 // black listed style attributes.
44305 Roo.HtmlEditorCore.cblack= [
44306       //  'font-size' -- this can be set by the project 
44307 ];
44308
44309
44310 Roo.HtmlEditorCore.swapCodes   =[ 
44311     [    8211, "--" ], 
44312     [    8212, "--" ], 
44313     [    8216,  "'" ],  
44314     [    8217, "'" ],  
44315     [    8220, '"' ],  
44316     [    8221, '"' ],  
44317     [    8226, "*" ],  
44318     [    8230, "..." ]
44319 ]; 
44320
44321     //<script type="text/javascript">
44322
44323 /*
44324  * Ext JS Library 1.1.1
44325  * Copyright(c) 2006-2007, Ext JS, LLC.
44326  * Licence LGPL
44327  * 
44328  */
44329  
44330  
44331 Roo.form.HtmlEditor = function(config){
44332     
44333     
44334     
44335     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44336     
44337     if (!this.toolbars) {
44338         this.toolbars = [];
44339     }
44340     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44341     
44342     
44343 };
44344
44345 /**
44346  * @class Roo.form.HtmlEditor
44347  * @extends Roo.form.Field
44348  * Provides a lightweight HTML Editor component.
44349  *
44350  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44351  * 
44352  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44353  * supported by this editor.</b><br/><br/>
44354  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44355  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44356  */
44357 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44358     /**
44359      * @cfg {Boolean} clearUp
44360      */
44361     clearUp : true,
44362       /**
44363      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44364      */
44365     toolbars : false,
44366    
44367      /**
44368      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44369      *                        Roo.resizable.
44370      */
44371     resizable : false,
44372      /**
44373      * @cfg {Number} height (in pixels)
44374      */   
44375     height: 300,
44376    /**
44377      * @cfg {Number} width (in pixels)
44378      */   
44379     width: 500,
44380     
44381     /**
44382      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44383      * 
44384      */
44385     stylesheets: false,
44386     
44387     
44388      /**
44389      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44390      * 
44391      */
44392     cblack: false,
44393     /**
44394      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44395      * 
44396      */
44397     cwhite: false,
44398     
44399      /**
44400      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44401      * 
44402      */
44403     black: false,
44404     /**
44405      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44406      * 
44407      */
44408     white: false,
44409     
44410     // id of frame..
44411     frameId: false,
44412     
44413     // private properties
44414     validationEvent : false,
44415     deferHeight: true,
44416     initialized : false,
44417     activated : false,
44418     
44419     onFocus : Roo.emptyFn,
44420     iframePad:3,
44421     hideMode:'offsets',
44422     
44423     actionMode : 'container', // defaults to hiding it...
44424     
44425     defaultAutoCreate : { // modified by initCompnoent..
44426         tag: "textarea",
44427         style:"width:500px;height:300px;",
44428         autocomplete: "new-password"
44429     },
44430
44431     // private
44432     initComponent : function(){
44433         this.addEvents({
44434             /**
44435              * @event initialize
44436              * Fires when the editor is fully initialized (including the iframe)
44437              * @param {HtmlEditor} this
44438              */
44439             initialize: true,
44440             /**
44441              * @event activate
44442              * Fires when the editor is first receives the focus. Any insertion must wait
44443              * until after this event.
44444              * @param {HtmlEditor} this
44445              */
44446             activate: true,
44447              /**
44448              * @event beforesync
44449              * Fires before the textarea is updated with content from the editor iframe. Return false
44450              * to cancel the sync.
44451              * @param {HtmlEditor} this
44452              * @param {String} html
44453              */
44454             beforesync: true,
44455              /**
44456              * @event beforepush
44457              * Fires before the iframe editor is updated with content from the textarea. Return false
44458              * to cancel the push.
44459              * @param {HtmlEditor} this
44460              * @param {String} html
44461              */
44462             beforepush: true,
44463              /**
44464              * @event sync
44465              * Fires when the textarea is updated with content from the editor iframe.
44466              * @param {HtmlEditor} this
44467              * @param {String} html
44468              */
44469             sync: true,
44470              /**
44471              * @event push
44472              * Fires when the iframe editor is updated with content from the textarea.
44473              * @param {HtmlEditor} this
44474              * @param {String} html
44475              */
44476             push: true,
44477              /**
44478              * @event editmodechange
44479              * Fires when the editor switches edit modes
44480              * @param {HtmlEditor} this
44481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44482              */
44483             editmodechange: true,
44484             /**
44485              * @event editorevent
44486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44487              * @param {HtmlEditor} this
44488              */
44489             editorevent: true,
44490             /**
44491              * @event firstfocus
44492              * Fires when on first focus - needed by toolbars..
44493              * @param {HtmlEditor} this
44494              */
44495             firstfocus: true,
44496             /**
44497              * @event autosave
44498              * Auto save the htmlEditor value as a file into Events
44499              * @param {HtmlEditor} this
44500              */
44501             autosave: true,
44502             /**
44503              * @event savedpreview
44504              * preview the saved version of htmlEditor
44505              * @param {HtmlEditor} this
44506              */
44507             savedpreview: true,
44508             
44509             /**
44510             * @event stylesheetsclick
44511             * Fires when press the Sytlesheets button
44512             * @param {Roo.HtmlEditorCore} this
44513             */
44514             stylesheetsclick: true
44515         });
44516         this.defaultAutoCreate =  {
44517             tag: "textarea",
44518             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44519             autocomplete: "new-password"
44520         };
44521     },
44522
44523     /**
44524      * Protected method that will not generally be called directly. It
44525      * is called when the editor creates its toolbar. Override this method if you need to
44526      * add custom toolbar buttons.
44527      * @param {HtmlEditor} editor
44528      */
44529     createToolbar : function(editor){
44530         Roo.log("create toolbars");
44531         if (!editor.toolbars || !editor.toolbars.length) {
44532             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44533         }
44534         
44535         for (var i =0 ; i < editor.toolbars.length;i++) {
44536             editor.toolbars[i] = Roo.factory(
44537                     typeof(editor.toolbars[i]) == 'string' ?
44538                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44539                 Roo.form.HtmlEditor);
44540             editor.toolbars[i].init(editor);
44541         }
44542          
44543         
44544     },
44545
44546      
44547     // private
44548     onRender : function(ct, position)
44549     {
44550         var _t = this;
44551         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44552         
44553         this.wrap = this.el.wrap({
44554             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44555         });
44556         
44557         this.editorcore.onRender(ct, position);
44558          
44559         if (this.resizable) {
44560             this.resizeEl = new Roo.Resizable(this.wrap, {
44561                 pinned : true,
44562                 wrap: true,
44563                 dynamic : true,
44564                 minHeight : this.height,
44565                 height: this.height,
44566                 handles : this.resizable,
44567                 width: this.width,
44568                 listeners : {
44569                     resize : function(r, w, h) {
44570                         _t.onResize(w,h); // -something
44571                     }
44572                 }
44573             });
44574             
44575         }
44576         this.createToolbar(this);
44577        
44578         
44579         if(!this.width){
44580             this.setSize(this.wrap.getSize());
44581         }
44582         if (this.resizeEl) {
44583             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44584             // should trigger onReize..
44585         }
44586         
44587         this.keyNav = new Roo.KeyNav(this.el, {
44588             
44589             "tab" : function(e){
44590                 e.preventDefault();
44591                 
44592                 var value = this.getValue();
44593                 
44594                 var start = this.el.dom.selectionStart;
44595                 var end = this.el.dom.selectionEnd;
44596                 
44597                 if(!e.shiftKey){
44598                     
44599                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44600                     this.el.dom.setSelectionRange(end + 1, end + 1);
44601                     return;
44602                 }
44603                 
44604                 var f = value.substring(0, start).split("\t");
44605                 
44606                 if(f.pop().length != 0){
44607                     return;
44608                 }
44609                 
44610                 this.setValue(f.join("\t") + value.substring(end));
44611                 this.el.dom.setSelectionRange(start - 1, start - 1);
44612                 
44613             },
44614             
44615             "home" : function(e){
44616                 e.preventDefault();
44617                 
44618                 var curr = this.el.dom.selectionStart;
44619                 var lines = this.getValue().split("\n");
44620                 
44621                 if(!lines.length){
44622                     return;
44623                 }
44624                 
44625                 if(e.ctrlKey){
44626                     this.el.dom.setSelectionRange(0, 0);
44627                     return;
44628                 }
44629                 
44630                 var pos = 0;
44631                 
44632                 for (var i = 0; i < lines.length;i++) {
44633                     pos += lines[i].length;
44634                     
44635                     if(i != 0){
44636                         pos += 1;
44637                     }
44638                     
44639                     if(pos < curr){
44640                         continue;
44641                     }
44642                     
44643                     pos -= lines[i].length;
44644                     
44645                     break;
44646                 }
44647                 
44648                 if(!e.shiftKey){
44649                     this.el.dom.setSelectionRange(pos, pos);
44650                     return;
44651                 }
44652                 
44653                 this.el.dom.selectionStart = pos;
44654                 this.el.dom.selectionEnd = curr;
44655             },
44656             
44657             "end" : function(e){
44658                 e.preventDefault();
44659                 
44660                 var curr = this.el.dom.selectionStart;
44661                 var lines = this.getValue().split("\n");
44662                 
44663                 if(!lines.length){
44664                     return;
44665                 }
44666                 
44667                 if(e.ctrlKey){
44668                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44669                     return;
44670                 }
44671                 
44672                 var pos = 0;
44673                 
44674                 for (var i = 0; i < lines.length;i++) {
44675                     
44676                     pos += lines[i].length;
44677                     
44678                     if(i != 0){
44679                         pos += 1;
44680                     }
44681                     
44682                     if(pos < curr){
44683                         continue;
44684                     }
44685                     
44686                     break;
44687                 }
44688                 
44689                 if(!e.shiftKey){
44690                     this.el.dom.setSelectionRange(pos, pos);
44691                     return;
44692                 }
44693                 
44694                 this.el.dom.selectionStart = curr;
44695                 this.el.dom.selectionEnd = pos;
44696             },
44697
44698             scope : this,
44699
44700             doRelay : function(foo, bar, hname){
44701                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44702             },
44703
44704             forceKeyDown: true
44705         });
44706         
44707 //        if(this.autosave && this.w){
44708 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44709 //        }
44710     },
44711
44712     // private
44713     onResize : function(w, h)
44714     {
44715         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44716         var ew = false;
44717         var eh = false;
44718         
44719         if(this.el ){
44720             if(typeof w == 'number'){
44721                 var aw = w - this.wrap.getFrameWidth('lr');
44722                 this.el.setWidth(this.adjustWidth('textarea', aw));
44723                 ew = aw;
44724             }
44725             if(typeof h == 'number'){
44726                 var tbh = 0;
44727                 for (var i =0; i < this.toolbars.length;i++) {
44728                     // fixme - ask toolbars for heights?
44729                     tbh += this.toolbars[i].tb.el.getHeight();
44730                     if (this.toolbars[i].footer) {
44731                         tbh += this.toolbars[i].footer.el.getHeight();
44732                     }
44733                 }
44734                 
44735                 
44736                 
44737                 
44738                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44739                 ah -= 5; // knock a few pixes off for look..
44740 //                Roo.log(ah);
44741                 this.el.setHeight(this.adjustWidth('textarea', ah));
44742                 var eh = ah;
44743             }
44744         }
44745         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44746         this.editorcore.onResize(ew,eh);
44747         
44748     },
44749
44750     /**
44751      * Toggles the editor between standard and source edit mode.
44752      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44753      */
44754     toggleSourceEdit : function(sourceEditMode)
44755     {
44756         this.editorcore.toggleSourceEdit(sourceEditMode);
44757         
44758         if(this.editorcore.sourceEditMode){
44759             Roo.log('editor - showing textarea');
44760             
44761 //            Roo.log('in');
44762 //            Roo.log(this.syncValue());
44763             this.editorcore.syncValue();
44764             this.el.removeClass('x-hidden');
44765             this.el.dom.removeAttribute('tabIndex');
44766             this.el.focus();
44767             
44768             for (var i = 0; i < this.toolbars.length; i++) {
44769                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44770                     this.toolbars[i].tb.hide();
44771                     this.toolbars[i].footer.hide();
44772                 }
44773             }
44774             
44775         }else{
44776             Roo.log('editor - hiding textarea');
44777 //            Roo.log('out')
44778 //            Roo.log(this.pushValue()); 
44779             this.editorcore.pushValue();
44780             
44781             this.el.addClass('x-hidden');
44782             this.el.dom.setAttribute('tabIndex', -1);
44783             
44784             for (var i = 0; i < this.toolbars.length; i++) {
44785                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44786                     this.toolbars[i].tb.show();
44787                     this.toolbars[i].footer.show();
44788                 }
44789             }
44790             
44791             //this.deferFocus();
44792         }
44793         
44794         this.setSize(this.wrap.getSize());
44795         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44796         
44797         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44798     },
44799  
44800     // private (for BoxComponent)
44801     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44802
44803     // private (for BoxComponent)
44804     getResizeEl : function(){
44805         return this.wrap;
44806     },
44807
44808     // private (for BoxComponent)
44809     getPositionEl : function(){
44810         return this.wrap;
44811     },
44812
44813     // private
44814     initEvents : function(){
44815         this.originalValue = this.getValue();
44816     },
44817
44818     /**
44819      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44820      * @method
44821      */
44822     markInvalid : Roo.emptyFn,
44823     /**
44824      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44825      * @method
44826      */
44827     clearInvalid : Roo.emptyFn,
44828
44829     setValue : function(v){
44830         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44831         this.editorcore.pushValue();
44832     },
44833
44834      
44835     // private
44836     deferFocus : function(){
44837         this.focus.defer(10, this);
44838     },
44839
44840     // doc'ed in Field
44841     focus : function(){
44842         this.editorcore.focus();
44843         
44844     },
44845       
44846
44847     // private
44848     onDestroy : function(){
44849         
44850         
44851         
44852         if(this.rendered){
44853             
44854             for (var i =0; i < this.toolbars.length;i++) {
44855                 // fixme - ask toolbars for heights?
44856                 this.toolbars[i].onDestroy();
44857             }
44858             
44859             this.wrap.dom.innerHTML = '';
44860             this.wrap.remove();
44861         }
44862     },
44863
44864     // private
44865     onFirstFocus : function(){
44866         //Roo.log("onFirstFocus");
44867         this.editorcore.onFirstFocus();
44868          for (var i =0; i < this.toolbars.length;i++) {
44869             this.toolbars[i].onFirstFocus();
44870         }
44871         
44872     },
44873     
44874     // private
44875     syncValue : function()
44876     {
44877         this.editorcore.syncValue();
44878     },
44879     
44880     pushValue : function()
44881     {
44882         this.editorcore.pushValue();
44883     },
44884     
44885     setStylesheets : function(stylesheets)
44886     {
44887         this.editorcore.setStylesheets(stylesheets);
44888     },
44889     
44890     removeStylesheets : function()
44891     {
44892         this.editorcore.removeStylesheets();
44893     }
44894      
44895     
44896     // hide stuff that is not compatible
44897     /**
44898      * @event blur
44899      * @hide
44900      */
44901     /**
44902      * @event change
44903      * @hide
44904      */
44905     /**
44906      * @event focus
44907      * @hide
44908      */
44909     /**
44910      * @event specialkey
44911      * @hide
44912      */
44913     /**
44914      * @cfg {String} fieldClass @hide
44915      */
44916     /**
44917      * @cfg {String} focusClass @hide
44918      */
44919     /**
44920      * @cfg {String} autoCreate @hide
44921      */
44922     /**
44923      * @cfg {String} inputType @hide
44924      */
44925     /**
44926      * @cfg {String} invalidClass @hide
44927      */
44928     /**
44929      * @cfg {String} invalidText @hide
44930      */
44931     /**
44932      * @cfg {String} msgFx @hide
44933      */
44934     /**
44935      * @cfg {String} validateOnBlur @hide
44936      */
44937 });
44938  
44939     // <script type="text/javascript">
44940 /*
44941  * Based on
44942  * Ext JS Library 1.1.1
44943  * Copyright(c) 2006-2007, Ext JS, LLC.
44944  *  
44945  
44946  */
44947
44948 /**
44949  * @class Roo.form.HtmlEditorToolbar1
44950  * Basic Toolbar
44951  * 
44952  * Usage:
44953  *
44954  new Roo.form.HtmlEditor({
44955     ....
44956     toolbars : [
44957         new Roo.form.HtmlEditorToolbar1({
44958             disable : { fonts: 1 , format: 1, ..., ... , ...],
44959             btns : [ .... ]
44960         })
44961     }
44962      
44963  * 
44964  * @cfg {Object} disable List of elements to disable..
44965  * @cfg {Array} btns List of additional buttons.
44966  * 
44967  * 
44968  * NEEDS Extra CSS? 
44969  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44970  */
44971  
44972 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44973 {
44974     
44975     Roo.apply(this, config);
44976     
44977     // default disabled, based on 'good practice'..
44978     this.disable = this.disable || {};
44979     Roo.applyIf(this.disable, {
44980         fontSize : true,
44981         colors : true,
44982         specialElements : true
44983     });
44984     
44985     
44986     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44987     // dont call parent... till later.
44988 }
44989
44990 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44991     
44992     tb: false,
44993     
44994     rendered: false,
44995     
44996     editor : false,
44997     editorcore : false,
44998     /**
44999      * @cfg {Object} disable  List of toolbar elements to disable
45000          
45001      */
45002     disable : false,
45003     
45004     
45005      /**
45006      * @cfg {String} createLinkText The default text for the create link prompt
45007      */
45008     createLinkText : 'Please enter the URL for the link:',
45009     /**
45010      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45011      */
45012     defaultLinkValue : 'http:/'+'/',
45013    
45014     
45015       /**
45016      * @cfg {Array} fontFamilies An array of available font families
45017      */
45018     fontFamilies : [
45019         'Arial',
45020         'Courier New',
45021         'Tahoma',
45022         'Times New Roman',
45023         'Verdana'
45024     ],
45025     
45026     specialChars : [
45027            "&#169;",
45028           "&#174;",     
45029           "&#8482;",    
45030           "&#163;" ,    
45031          // "&#8212;",    
45032           "&#8230;",    
45033           "&#247;" ,    
45034         //  "&#225;" ,     ?? a acute?
45035            "&#8364;"    , //Euro
45036        //   "&#8220;"    ,
45037         //  "&#8221;"    ,
45038         //  "&#8226;"    ,
45039           "&#176;"  //   , // degrees
45040
45041          // "&#233;"     , // e ecute
45042          // "&#250;"     , // u ecute?
45043     ],
45044     
45045     specialElements : [
45046         {
45047             text: "Insert Table",
45048             xtype: 'MenuItem',
45049             xns : Roo.Menu,
45050             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45051                 
45052         },
45053         {    
45054             text: "Insert Image",
45055             xtype: 'MenuItem',
45056             xns : Roo.Menu,
45057             ihtml : '<img src="about:blank"/>'
45058             
45059         }
45060         
45061          
45062     ],
45063     
45064     
45065     inputElements : [ 
45066             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45067             "input:submit", "input:button", "select", "textarea", "label" ],
45068     formats : [
45069         ["p"] ,  
45070         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45071         ["pre"],[ "code"], 
45072         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45073         ['div'],['span']
45074     ],
45075     
45076     cleanStyles : [
45077         "font-size"
45078     ],
45079      /**
45080      * @cfg {String} defaultFont default font to use.
45081      */
45082     defaultFont: 'tahoma',
45083    
45084     fontSelect : false,
45085     
45086     
45087     formatCombo : false,
45088     
45089     init : function(editor)
45090     {
45091         this.editor = editor;
45092         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45093         var editorcore = this.editorcore;
45094         
45095         var _t = this;
45096         
45097         var fid = editorcore.frameId;
45098         var etb = this;
45099         function btn(id, toggle, handler){
45100             var xid = fid + '-'+ id ;
45101             return {
45102                 id : xid,
45103                 cmd : id,
45104                 cls : 'x-btn-icon x-edit-'+id,
45105                 enableToggle:toggle !== false,
45106                 scope: _t, // was editor...
45107                 handler:handler||_t.relayBtnCmd,
45108                 clickEvent:'mousedown',
45109                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45110                 tabIndex:-1
45111             };
45112         }
45113         
45114         
45115         
45116         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45117         this.tb = tb;
45118          // stop form submits
45119         tb.el.on('click', function(e){
45120             e.preventDefault(); // what does this do?
45121         });
45122
45123         if(!this.disable.font) { // && !Roo.isSafari){
45124             /* why no safari for fonts 
45125             editor.fontSelect = tb.el.createChild({
45126                 tag:'select',
45127                 tabIndex: -1,
45128                 cls:'x-font-select',
45129                 html: this.createFontOptions()
45130             });
45131             
45132             editor.fontSelect.on('change', function(){
45133                 var font = editor.fontSelect.dom.value;
45134                 editor.relayCmd('fontname', font);
45135                 editor.deferFocus();
45136             }, editor);
45137             
45138             tb.add(
45139                 editor.fontSelect.dom,
45140                 '-'
45141             );
45142             */
45143             
45144         };
45145         if(!this.disable.formats){
45146             this.formatCombo = new Roo.form.ComboBox({
45147                 store: new Roo.data.SimpleStore({
45148                     id : 'tag',
45149                     fields: ['tag'],
45150                     data : this.formats // from states.js
45151                 }),
45152                 blockFocus : true,
45153                 name : '',
45154                 //autoCreate : {tag: "div",  size: "20"},
45155                 displayField:'tag',
45156                 typeAhead: false,
45157                 mode: 'local',
45158                 editable : false,
45159                 triggerAction: 'all',
45160                 emptyText:'Add tag',
45161                 selectOnFocus:true,
45162                 width:135,
45163                 listeners : {
45164                     'select': function(c, r, i) {
45165                         editorcore.insertTag(r.get('tag'));
45166                         editor.focus();
45167                     }
45168                 }
45169
45170             });
45171             tb.addField(this.formatCombo);
45172             
45173         }
45174         
45175         if(!this.disable.format){
45176             tb.add(
45177                 btn('bold'),
45178                 btn('italic'),
45179                 btn('underline'),
45180                 btn('strikethrough')
45181             );
45182         };
45183         if(!this.disable.fontSize){
45184             tb.add(
45185                 '-',
45186                 
45187                 
45188                 btn('increasefontsize', false, editorcore.adjustFont),
45189                 btn('decreasefontsize', false, editorcore.adjustFont)
45190             );
45191         };
45192         
45193         
45194         if(!this.disable.colors){
45195             tb.add(
45196                 '-', {
45197                     id:editorcore.frameId +'-forecolor',
45198                     cls:'x-btn-icon x-edit-forecolor',
45199                     clickEvent:'mousedown',
45200                     tooltip: this.buttonTips['forecolor'] || undefined,
45201                     tabIndex:-1,
45202                     menu : new Roo.menu.ColorMenu({
45203                         allowReselect: true,
45204                         focus: Roo.emptyFn,
45205                         value:'000000',
45206                         plain:true,
45207                         selectHandler: function(cp, color){
45208                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45209                             editor.deferFocus();
45210                         },
45211                         scope: editorcore,
45212                         clickEvent:'mousedown'
45213                     })
45214                 }, {
45215                     id:editorcore.frameId +'backcolor',
45216                     cls:'x-btn-icon x-edit-backcolor',
45217                     clickEvent:'mousedown',
45218                     tooltip: this.buttonTips['backcolor'] || undefined,
45219                     tabIndex:-1,
45220                     menu : new Roo.menu.ColorMenu({
45221                         focus: Roo.emptyFn,
45222                         value:'FFFFFF',
45223                         plain:true,
45224                         allowReselect: true,
45225                         selectHandler: function(cp, color){
45226                             if(Roo.isGecko){
45227                                 editorcore.execCmd('useCSS', false);
45228                                 editorcore.execCmd('hilitecolor', color);
45229                                 editorcore.execCmd('useCSS', true);
45230                                 editor.deferFocus();
45231                             }else{
45232                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45233                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45234                                 editor.deferFocus();
45235                             }
45236                         },
45237                         scope:editorcore,
45238                         clickEvent:'mousedown'
45239                     })
45240                 }
45241             );
45242         };
45243         // now add all the items...
45244         
45245
45246         if(!this.disable.alignments){
45247             tb.add(
45248                 '-',
45249                 btn('justifyleft'),
45250                 btn('justifycenter'),
45251                 btn('justifyright')
45252             );
45253         };
45254
45255         //if(!Roo.isSafari){
45256             if(!this.disable.links){
45257                 tb.add(
45258                     '-',
45259                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45260                 );
45261             };
45262
45263             if(!this.disable.lists){
45264                 tb.add(
45265                     '-',
45266                     btn('insertorderedlist'),
45267                     btn('insertunorderedlist')
45268                 );
45269             }
45270             if(!this.disable.sourceEdit){
45271                 tb.add(
45272                     '-',
45273                     btn('sourceedit', true, function(btn){
45274                         this.toggleSourceEdit(btn.pressed);
45275                     })
45276                 );
45277             }
45278         //}
45279         
45280         var smenu = { };
45281         // special menu.. - needs to be tidied up..
45282         if (!this.disable.special) {
45283             smenu = {
45284                 text: "&#169;",
45285                 cls: 'x-edit-none',
45286                 
45287                 menu : {
45288                     items : []
45289                 }
45290             };
45291             for (var i =0; i < this.specialChars.length; i++) {
45292                 smenu.menu.items.push({
45293                     
45294                     html: this.specialChars[i],
45295                     handler: function(a,b) {
45296                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45297                         //editor.insertAtCursor(a.html);
45298                         
45299                     },
45300                     tabIndex:-1
45301                 });
45302             }
45303             
45304             
45305             tb.add(smenu);
45306             
45307             
45308         }
45309         
45310         var cmenu = { };
45311         if (!this.disable.cleanStyles) {
45312             cmenu = {
45313                 cls: 'x-btn-icon x-btn-clear',
45314                 
45315                 menu : {
45316                     items : []
45317                 }
45318             };
45319             for (var i =0; i < this.cleanStyles.length; i++) {
45320                 cmenu.menu.items.push({
45321                     actiontype : this.cleanStyles[i],
45322                     html: 'Remove ' + this.cleanStyles[i],
45323                     handler: function(a,b) {
45324 //                        Roo.log(a);
45325 //                        Roo.log(b);
45326                         var c = Roo.get(editorcore.doc.body);
45327                         c.select('[style]').each(function(s) {
45328                             s.dom.style.removeProperty(a.actiontype);
45329                         });
45330                         editorcore.syncValue();
45331                     },
45332                     tabIndex:-1
45333                 });
45334             }
45335              cmenu.menu.items.push({
45336                 actiontype : 'tablewidths',
45337                 html: 'Remove Table Widths',
45338                 handler: function(a,b) {
45339                     editorcore.cleanTableWidths();
45340                     editorcore.syncValue();
45341                 },
45342                 tabIndex:-1
45343             });
45344             cmenu.menu.items.push({
45345                 actiontype : 'word',
45346                 html: 'Remove MS Word Formating',
45347                 handler: function(a,b) {
45348                     editorcore.cleanWord();
45349                     editorcore.syncValue();
45350                 },
45351                 tabIndex:-1
45352             });
45353             
45354             cmenu.menu.items.push({
45355                 actiontype : 'all',
45356                 html: 'Remove All Styles',
45357                 handler: function(a,b) {
45358                     
45359                     var c = Roo.get(editorcore.doc.body);
45360                     c.select('[style]').each(function(s) {
45361                         s.dom.removeAttribute('style');
45362                     });
45363                     editorcore.syncValue();
45364                 },
45365                 tabIndex:-1
45366             });
45367             
45368             cmenu.menu.items.push({
45369                 actiontype : 'all',
45370                 html: 'Remove All CSS Classes',
45371                 handler: function(a,b) {
45372                     
45373                     var c = Roo.get(editorcore.doc.body);
45374                     c.select('[class]').each(function(s) {
45375                         s.dom.className = '';
45376                     });
45377                     editorcore.syncValue();
45378                 },
45379                 tabIndex:-1
45380             });
45381             
45382              cmenu.menu.items.push({
45383                 actiontype : 'tidy',
45384                 html: 'Tidy HTML Source',
45385                 handler: function(a,b) {
45386                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45387                     editorcore.syncValue();
45388                 },
45389                 tabIndex:-1
45390             });
45391             
45392             
45393             tb.add(cmenu);
45394         }
45395          
45396         if (!this.disable.specialElements) {
45397             var semenu = {
45398                 text: "Other;",
45399                 cls: 'x-edit-none',
45400                 menu : {
45401                     items : []
45402                 }
45403             };
45404             for (var i =0; i < this.specialElements.length; i++) {
45405                 semenu.menu.items.push(
45406                     Roo.apply({ 
45407                         handler: function(a,b) {
45408                             editor.insertAtCursor(this.ihtml);
45409                         }
45410                     }, this.specialElements[i])
45411                 );
45412                     
45413             }
45414             
45415             tb.add(semenu);
45416             
45417             
45418         }
45419          
45420         
45421         if (this.btns) {
45422             for(var i =0; i< this.btns.length;i++) {
45423                 var b = Roo.factory(this.btns[i],Roo.form);
45424                 b.cls =  'x-edit-none';
45425                 
45426                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45427                     b.cls += ' x-init-enable';
45428                 }
45429                 
45430                 b.scope = editorcore;
45431                 tb.add(b);
45432             }
45433         
45434         }
45435         
45436         
45437         
45438         // disable everything...
45439         
45440         this.tb.items.each(function(item){
45441             
45442            if(
45443                 item.id != editorcore.frameId+ '-sourceedit' && 
45444                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45445             ){
45446                 
45447                 item.disable();
45448             }
45449         });
45450         this.rendered = true;
45451         
45452         // the all the btns;
45453         editor.on('editorevent', this.updateToolbar, this);
45454         // other toolbars need to implement this..
45455         //editor.on('editmodechange', this.updateToolbar, this);
45456     },
45457     
45458     
45459     relayBtnCmd : function(btn) {
45460         this.editorcore.relayCmd(btn.cmd);
45461     },
45462     // private used internally
45463     createLink : function(){
45464         Roo.log("create link?");
45465         var url = prompt(this.createLinkText, this.defaultLinkValue);
45466         if(url && url != 'http:/'+'/'){
45467             this.editorcore.relayCmd('createlink', url);
45468         }
45469     },
45470
45471     
45472     /**
45473      * Protected method that will not generally be called directly. It triggers
45474      * a toolbar update by reading the markup state of the current selection in the editor.
45475      */
45476     updateToolbar: function(){
45477
45478         if(!this.editorcore.activated){
45479             this.editor.onFirstFocus();
45480             return;
45481         }
45482
45483         var btns = this.tb.items.map, 
45484             doc = this.editorcore.doc,
45485             frameId = this.editorcore.frameId;
45486
45487         if(!this.disable.font && !Roo.isSafari){
45488             /*
45489             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45490             if(name != this.fontSelect.dom.value){
45491                 this.fontSelect.dom.value = name;
45492             }
45493             */
45494         }
45495         if(!this.disable.format){
45496             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45497             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45498             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45499             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45500         }
45501         if(!this.disable.alignments){
45502             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45503             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45504             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45505         }
45506         if(!Roo.isSafari && !this.disable.lists){
45507             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45508             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45509         }
45510         
45511         var ans = this.editorcore.getAllAncestors();
45512         if (this.formatCombo) {
45513             
45514             
45515             var store = this.formatCombo.store;
45516             this.formatCombo.setValue("");
45517             for (var i =0; i < ans.length;i++) {
45518                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45519                     // select it..
45520                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45521                     break;
45522                 }
45523             }
45524         }
45525         
45526         
45527         
45528         // hides menus... - so this cant be on a menu...
45529         Roo.menu.MenuMgr.hideAll();
45530
45531         //this.editorsyncValue();
45532     },
45533    
45534     
45535     createFontOptions : function(){
45536         var buf = [], fs = this.fontFamilies, ff, lc;
45537         
45538         
45539         
45540         for(var i = 0, len = fs.length; i< len; i++){
45541             ff = fs[i];
45542             lc = ff.toLowerCase();
45543             buf.push(
45544                 '<option value="',lc,'" style="font-family:',ff,';"',
45545                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45546                     ff,
45547                 '</option>'
45548             );
45549         }
45550         return buf.join('');
45551     },
45552     
45553     toggleSourceEdit : function(sourceEditMode){
45554         
45555         Roo.log("toolbar toogle");
45556         if(sourceEditMode === undefined){
45557             sourceEditMode = !this.sourceEditMode;
45558         }
45559         this.sourceEditMode = sourceEditMode === true;
45560         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45561         // just toggle the button?
45562         if(btn.pressed !== this.sourceEditMode){
45563             btn.toggle(this.sourceEditMode);
45564             return;
45565         }
45566         
45567         if(sourceEditMode){
45568             Roo.log("disabling buttons");
45569             this.tb.items.each(function(item){
45570                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45571                     item.disable();
45572                 }
45573             });
45574           
45575         }else{
45576             Roo.log("enabling buttons");
45577             if(this.editorcore.initialized){
45578                 this.tb.items.each(function(item){
45579                     item.enable();
45580                 });
45581             }
45582             
45583         }
45584         Roo.log("calling toggole on editor");
45585         // tell the editor that it's been pressed..
45586         this.editor.toggleSourceEdit(sourceEditMode);
45587        
45588     },
45589      /**
45590      * Object collection of toolbar tooltips for the buttons in the editor. The key
45591      * is the command id associated with that button and the value is a valid QuickTips object.
45592      * For example:
45593 <pre><code>
45594 {
45595     bold : {
45596         title: 'Bold (Ctrl+B)',
45597         text: 'Make the selected text bold.',
45598         cls: 'x-html-editor-tip'
45599     },
45600     italic : {
45601         title: 'Italic (Ctrl+I)',
45602         text: 'Make the selected text italic.',
45603         cls: 'x-html-editor-tip'
45604     },
45605     ...
45606 </code></pre>
45607     * @type Object
45608      */
45609     buttonTips : {
45610         bold : {
45611             title: 'Bold (Ctrl+B)',
45612             text: 'Make the selected text bold.',
45613             cls: 'x-html-editor-tip'
45614         },
45615         italic : {
45616             title: 'Italic (Ctrl+I)',
45617             text: 'Make the selected text italic.',
45618             cls: 'x-html-editor-tip'
45619         },
45620         underline : {
45621             title: 'Underline (Ctrl+U)',
45622             text: 'Underline the selected text.',
45623             cls: 'x-html-editor-tip'
45624         },
45625         strikethrough : {
45626             title: 'Strikethrough',
45627             text: 'Strikethrough the selected text.',
45628             cls: 'x-html-editor-tip'
45629         },
45630         increasefontsize : {
45631             title: 'Grow Text',
45632             text: 'Increase the font size.',
45633             cls: 'x-html-editor-tip'
45634         },
45635         decreasefontsize : {
45636             title: 'Shrink Text',
45637             text: 'Decrease the font size.',
45638             cls: 'x-html-editor-tip'
45639         },
45640         backcolor : {
45641             title: 'Text Highlight Color',
45642             text: 'Change the background color of the selected text.',
45643             cls: 'x-html-editor-tip'
45644         },
45645         forecolor : {
45646             title: 'Font Color',
45647             text: 'Change the color of the selected text.',
45648             cls: 'x-html-editor-tip'
45649         },
45650         justifyleft : {
45651             title: 'Align Text Left',
45652             text: 'Align text to the left.',
45653             cls: 'x-html-editor-tip'
45654         },
45655         justifycenter : {
45656             title: 'Center Text',
45657             text: 'Center text in the editor.',
45658             cls: 'x-html-editor-tip'
45659         },
45660         justifyright : {
45661             title: 'Align Text Right',
45662             text: 'Align text to the right.',
45663             cls: 'x-html-editor-tip'
45664         },
45665         insertunorderedlist : {
45666             title: 'Bullet List',
45667             text: 'Start a bulleted list.',
45668             cls: 'x-html-editor-tip'
45669         },
45670         insertorderedlist : {
45671             title: 'Numbered List',
45672             text: 'Start a numbered list.',
45673             cls: 'x-html-editor-tip'
45674         },
45675         createlink : {
45676             title: 'Hyperlink',
45677             text: 'Make the selected text a hyperlink.',
45678             cls: 'x-html-editor-tip'
45679         },
45680         sourceedit : {
45681             title: 'Source Edit',
45682             text: 'Switch to source editing mode.',
45683             cls: 'x-html-editor-tip'
45684         }
45685     },
45686     // private
45687     onDestroy : function(){
45688         if(this.rendered){
45689             
45690             this.tb.items.each(function(item){
45691                 if(item.menu){
45692                     item.menu.removeAll();
45693                     if(item.menu.el){
45694                         item.menu.el.destroy();
45695                     }
45696                 }
45697                 item.destroy();
45698             });
45699              
45700         }
45701     },
45702     onFirstFocus: function() {
45703         this.tb.items.each(function(item){
45704            item.enable();
45705         });
45706     }
45707 });
45708
45709
45710
45711
45712 // <script type="text/javascript">
45713 /*
45714  * Based on
45715  * Ext JS Library 1.1.1
45716  * Copyright(c) 2006-2007, Ext JS, LLC.
45717  *  
45718  
45719  */
45720
45721  
45722 /**
45723  * @class Roo.form.HtmlEditor.ToolbarContext
45724  * Context Toolbar
45725  * 
45726  * Usage:
45727  *
45728  new Roo.form.HtmlEditor({
45729     ....
45730     toolbars : [
45731         { xtype: 'ToolbarStandard', styles : {} }
45732         { xtype: 'ToolbarContext', disable : {} }
45733     ]
45734 })
45735
45736      
45737  * 
45738  * @config : {Object} disable List of elements to disable.. (not done yet.)
45739  * @config : {Object} styles  Map of styles available.
45740  * 
45741  */
45742
45743 Roo.form.HtmlEditor.ToolbarContext = function(config)
45744 {
45745     
45746     Roo.apply(this, config);
45747     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45748     // dont call parent... till later.
45749     this.styles = this.styles || {};
45750 }
45751
45752  
45753
45754 Roo.form.HtmlEditor.ToolbarContext.types = {
45755     'IMG' : {
45756         width : {
45757             title: "Width",
45758             width: 40
45759         },
45760         height:  {
45761             title: "Height",
45762             width: 40
45763         },
45764         align: {
45765             title: "Align",
45766             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45767             width : 80
45768             
45769         },
45770         border: {
45771             title: "Border",
45772             width: 40
45773         },
45774         alt: {
45775             title: "Alt",
45776             width: 120
45777         },
45778         src : {
45779             title: "Src",
45780             width: 220
45781         }
45782         
45783     },
45784     'A' : {
45785         name : {
45786             title: "Name",
45787             width: 50
45788         },
45789         target:  {
45790             title: "Target",
45791             width: 120
45792         },
45793         href:  {
45794             title: "Href",
45795             width: 220
45796         } // border?
45797         
45798     },
45799     'TABLE' : {
45800         rows : {
45801             title: "Rows",
45802             width: 20
45803         },
45804         cols : {
45805             title: "Cols",
45806             width: 20
45807         },
45808         width : {
45809             title: "Width",
45810             width: 40
45811         },
45812         height : {
45813             title: "Height",
45814             width: 40
45815         },
45816         border : {
45817             title: "Border",
45818             width: 20
45819         }
45820     },
45821     'TD' : {
45822         width : {
45823             title: "Width",
45824             width: 40
45825         },
45826         height : {
45827             title: "Height",
45828             width: 40
45829         },   
45830         align: {
45831             title: "Align",
45832             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45833             width: 80
45834         },
45835         valign: {
45836             title: "Valign",
45837             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45838             width: 80
45839         },
45840         colspan: {
45841             title: "Colspan",
45842             width: 20
45843             
45844         },
45845          'font-family'  : {
45846             title : "Font",
45847             style : 'fontFamily',
45848             displayField: 'display',
45849             optname : 'font-family',
45850             width: 140
45851         }
45852     },
45853     'INPUT' : {
45854         name : {
45855             title: "name",
45856             width: 120
45857         },
45858         value : {
45859             title: "Value",
45860             width: 120
45861         },
45862         width : {
45863             title: "Width",
45864             width: 40
45865         }
45866     },
45867     'LABEL' : {
45868         'for' : {
45869             title: "For",
45870             width: 120
45871         }
45872     },
45873     'TEXTAREA' : {
45874           name : {
45875             title: "name",
45876             width: 120
45877         },
45878         rows : {
45879             title: "Rows",
45880             width: 20
45881         },
45882         cols : {
45883             title: "Cols",
45884             width: 20
45885         }
45886     },
45887     'SELECT' : {
45888         name : {
45889             title: "name",
45890             width: 120
45891         },
45892         selectoptions : {
45893             title: "Options",
45894             width: 200
45895         }
45896     },
45897     
45898     // should we really allow this??
45899     // should this just be 
45900     'BODY' : {
45901         title : {
45902             title: "Title",
45903             width: 200,
45904             disabled : true
45905         }
45906     },
45907     'SPAN' : {
45908         'font-family'  : {
45909             title : "Font",
45910             style : 'fontFamily',
45911             displayField: 'display',
45912             optname : 'font-family',
45913             width: 140
45914         }
45915     },
45916     'DIV' : {
45917         'font-family'  : {
45918             title : "Font",
45919             style : 'fontFamily',
45920             displayField: 'display',
45921             optname : 'font-family',
45922             width: 140
45923         }
45924     },
45925      'P' : {
45926         'font-family'  : {
45927             title : "Font",
45928             style : 'fontFamily',
45929             displayField: 'display',
45930             optname : 'font-family',
45931             width: 140
45932         }
45933     },
45934     
45935     '*' : {
45936         // empty..
45937     }
45938
45939 };
45940
45941 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45942 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45943
45944 Roo.form.HtmlEditor.ToolbarContext.options = {
45945         'font-family'  : [ 
45946                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45947                 [ 'Courier New', 'Courier New'],
45948                 [ 'Tahoma', 'Tahoma'],
45949                 [ 'Times New Roman,serif', 'Times'],
45950                 [ 'Verdana','Verdana' ]
45951         ]
45952 };
45953
45954 // fixme - these need to be configurable..
45955  
45956
45957 //Roo.form.HtmlEditor.ToolbarContext.types
45958
45959
45960 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45961     
45962     tb: false,
45963     
45964     rendered: false,
45965     
45966     editor : false,
45967     editorcore : false,
45968     /**
45969      * @cfg {Object} disable  List of toolbar elements to disable
45970          
45971      */
45972     disable : false,
45973     /**
45974      * @cfg {Object} styles List of styles 
45975      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45976      *
45977      * These must be defined in the page, so they get rendered correctly..
45978      * .headline { }
45979      * TD.underline { }
45980      * 
45981      */
45982     styles : false,
45983     
45984     options: false,
45985     
45986     toolbars : false,
45987     
45988     init : function(editor)
45989     {
45990         this.editor = editor;
45991         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45992         var editorcore = this.editorcore;
45993         
45994         var fid = editorcore.frameId;
45995         var etb = this;
45996         function btn(id, toggle, handler){
45997             var xid = fid + '-'+ id ;
45998             return {
45999                 id : xid,
46000                 cmd : id,
46001                 cls : 'x-btn-icon x-edit-'+id,
46002                 enableToggle:toggle !== false,
46003                 scope: editorcore, // was editor...
46004                 handler:handler||editorcore.relayBtnCmd,
46005                 clickEvent:'mousedown',
46006                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46007                 tabIndex:-1
46008             };
46009         }
46010         // create a new element.
46011         var wdiv = editor.wrap.createChild({
46012                 tag: 'div'
46013             }, editor.wrap.dom.firstChild.nextSibling, true);
46014         
46015         // can we do this more than once??
46016         
46017          // stop form submits
46018       
46019  
46020         // disable everything...
46021         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46022         this.toolbars = {};
46023            
46024         for (var i in  ty) {
46025           
46026             this.toolbars[i] = this.buildToolbar(ty[i],i);
46027         }
46028         this.tb = this.toolbars.BODY;
46029         this.tb.el.show();
46030         this.buildFooter();
46031         this.footer.show();
46032         editor.on('hide', function( ) { this.footer.hide() }, this);
46033         editor.on('show', function( ) { this.footer.show() }, this);
46034         
46035          
46036         this.rendered = true;
46037         
46038         // the all the btns;
46039         editor.on('editorevent', this.updateToolbar, this);
46040         // other toolbars need to implement this..
46041         //editor.on('editmodechange', this.updateToolbar, this);
46042     },
46043     
46044     
46045     
46046     /**
46047      * Protected method that will not generally be called directly. It triggers
46048      * a toolbar update by reading the markup state of the current selection in the editor.
46049      *
46050      * Note you can force an update by calling on('editorevent', scope, false)
46051      */
46052     updateToolbar: function(editor,ev,sel){
46053
46054         //Roo.log(ev);
46055         // capture mouse up - this is handy for selecting images..
46056         // perhaps should go somewhere else...
46057         if(!this.editorcore.activated){
46058              this.editor.onFirstFocus();
46059             return;
46060         }
46061         
46062         
46063         
46064         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46065         // selectNode - might want to handle IE?
46066         if (ev &&
46067             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46068             ev.target && ev.target.tagName == 'IMG') {
46069             // they have click on an image...
46070             // let's see if we can change the selection...
46071             sel = ev.target;
46072          
46073               var nodeRange = sel.ownerDocument.createRange();
46074             try {
46075                 nodeRange.selectNode(sel);
46076             } catch (e) {
46077                 nodeRange.selectNodeContents(sel);
46078             }
46079             //nodeRange.collapse(true);
46080             var s = this.editorcore.win.getSelection();
46081             s.removeAllRanges();
46082             s.addRange(nodeRange);
46083         }  
46084         
46085       
46086         var updateFooter = sel ? false : true;
46087         
46088         
46089         var ans = this.editorcore.getAllAncestors();
46090         
46091         // pick
46092         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46093         
46094         if (!sel) { 
46095             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46096             sel = sel ? sel : this.editorcore.doc.body;
46097             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46098             
46099         }
46100         // pick a menu that exists..
46101         var tn = sel.tagName.toUpperCase();
46102         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46103         
46104         tn = sel.tagName.toUpperCase();
46105         
46106         var lastSel = this.tb.selectedNode;
46107         
46108         this.tb.selectedNode = sel;
46109         
46110         // if current menu does not match..
46111         
46112         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46113                 
46114             this.tb.el.hide();
46115             ///console.log("show: " + tn);
46116             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46117             this.tb.el.show();
46118             // update name
46119             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46120             
46121             
46122             // update attributes
46123             if (this.tb.fields) {
46124                 this.tb.fields.each(function(e) {
46125                     if (e.stylename) {
46126                         e.setValue(sel.style[e.stylename]);
46127                         return;
46128                     } 
46129                    e.setValue(sel.getAttribute(e.attrname));
46130                 });
46131             }
46132             
46133             var hasStyles = false;
46134             for(var i in this.styles) {
46135                 hasStyles = true;
46136                 break;
46137             }
46138             
46139             // update styles
46140             if (hasStyles) { 
46141                 var st = this.tb.fields.item(0);
46142                 
46143                 st.store.removeAll();
46144                
46145                 
46146                 var cn = sel.className.split(/\s+/);
46147                 
46148                 var avs = [];
46149                 if (this.styles['*']) {
46150                     
46151                     Roo.each(this.styles['*'], function(v) {
46152                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46153                     });
46154                 }
46155                 if (this.styles[tn]) { 
46156                     Roo.each(this.styles[tn], function(v) {
46157                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46158                     });
46159                 }
46160                 
46161                 st.store.loadData(avs);
46162                 st.collapse();
46163                 st.setValue(cn);
46164             }
46165             // flag our selected Node.
46166             this.tb.selectedNode = sel;
46167            
46168            
46169             Roo.menu.MenuMgr.hideAll();
46170
46171         }
46172         
46173         if (!updateFooter) {
46174             //this.footDisp.dom.innerHTML = ''; 
46175             return;
46176         }
46177         // update the footer
46178         //
46179         var html = '';
46180         
46181         this.footerEls = ans.reverse();
46182         Roo.each(this.footerEls, function(a,i) {
46183             if (!a) { return; }
46184             html += html.length ? ' &gt; '  :  '';
46185             
46186             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46187             
46188         });
46189        
46190         // 
46191         var sz = this.footDisp.up('td').getSize();
46192         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46193         this.footDisp.dom.style.marginLeft = '5px';
46194         
46195         this.footDisp.dom.style.overflow = 'hidden';
46196         
46197         this.footDisp.dom.innerHTML = html;
46198             
46199         //this.editorsyncValue();
46200     },
46201      
46202     
46203    
46204        
46205     // private
46206     onDestroy : function(){
46207         if(this.rendered){
46208             
46209             this.tb.items.each(function(item){
46210                 if(item.menu){
46211                     item.menu.removeAll();
46212                     if(item.menu.el){
46213                         item.menu.el.destroy();
46214                     }
46215                 }
46216                 item.destroy();
46217             });
46218              
46219         }
46220     },
46221     onFirstFocus: function() {
46222         // need to do this for all the toolbars..
46223         this.tb.items.each(function(item){
46224            item.enable();
46225         });
46226     },
46227     buildToolbar: function(tlist, nm)
46228     {
46229         var editor = this.editor;
46230         var editorcore = this.editorcore;
46231          // create a new element.
46232         var wdiv = editor.wrap.createChild({
46233                 tag: 'div'
46234             }, editor.wrap.dom.firstChild.nextSibling, true);
46235         
46236        
46237         var tb = new Roo.Toolbar(wdiv);
46238         // add the name..
46239         
46240         tb.add(nm+ ":&nbsp;");
46241         
46242         var styles = [];
46243         for(var i in this.styles) {
46244             styles.push(i);
46245         }
46246         
46247         // styles...
46248         if (styles && styles.length) {
46249             
46250             // this needs a multi-select checkbox...
46251             tb.addField( new Roo.form.ComboBox({
46252                 store: new Roo.data.SimpleStore({
46253                     id : 'val',
46254                     fields: ['val', 'selected'],
46255                     data : [] 
46256                 }),
46257                 name : '-roo-edit-className',
46258                 attrname : 'className',
46259                 displayField: 'val',
46260                 typeAhead: false,
46261                 mode: 'local',
46262                 editable : false,
46263                 triggerAction: 'all',
46264                 emptyText:'Select Style',
46265                 selectOnFocus:true,
46266                 width: 130,
46267                 listeners : {
46268                     'select': function(c, r, i) {
46269                         // initial support only for on class per el..
46270                         tb.selectedNode.className =  r ? r.get('val') : '';
46271                         editorcore.syncValue();
46272                     }
46273                 }
46274     
46275             }));
46276         }
46277         
46278         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46279         var tbops = tbc.options;
46280         
46281         for (var i in tlist) {
46282             
46283             var item = tlist[i];
46284             tb.add(item.title + ":&nbsp;");
46285             
46286             
46287             //optname == used so you can configure the options available..
46288             var opts = item.opts ? item.opts : false;
46289             if (item.optname) {
46290                 opts = tbops[item.optname];
46291            
46292             }
46293             
46294             if (opts) {
46295                 // opts == pulldown..
46296                 tb.addField( new Roo.form.ComboBox({
46297                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46298                         id : 'val',
46299                         fields: ['val', 'display'],
46300                         data : opts  
46301                     }),
46302                     name : '-roo-edit-' + i,
46303                     attrname : i,
46304                     stylename : item.style ? item.style : false,
46305                     displayField: item.displayField ? item.displayField : 'val',
46306                     valueField :  'val',
46307                     typeAhead: false,
46308                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46309                     editable : false,
46310                     triggerAction: 'all',
46311                     emptyText:'Select',
46312                     selectOnFocus:true,
46313                     width: item.width ? item.width  : 130,
46314                     listeners : {
46315                         'select': function(c, r, i) {
46316                             if (c.stylename) {
46317                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46318                                 return;
46319                             }
46320                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46321                         }
46322                     }
46323
46324                 }));
46325                 continue;
46326                     
46327                  
46328                 
46329                 tb.addField( new Roo.form.TextField({
46330                     name: i,
46331                     width: 100,
46332                     //allowBlank:false,
46333                     value: ''
46334                 }));
46335                 continue;
46336             }
46337             tb.addField( new Roo.form.TextField({
46338                 name: '-roo-edit-' + i,
46339                 attrname : i,
46340                 
46341                 width: item.width,
46342                 //allowBlank:true,
46343                 value: '',
46344                 listeners: {
46345                     'change' : function(f, nv, ov) {
46346                         tb.selectedNode.setAttribute(f.attrname, nv);
46347                         editorcore.syncValue();
46348                     }
46349                 }
46350             }));
46351              
46352         }
46353         
46354         var _this = this;
46355         
46356         if(nm == 'BODY'){
46357             tb.addSeparator();
46358         
46359             tb.addButton( {
46360                 text: 'Stylesheets',
46361
46362                 listeners : {
46363                     click : function ()
46364                     {
46365                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46366                     }
46367                 }
46368             });
46369         }
46370         
46371         tb.addFill();
46372         tb.addButton( {
46373             text: 'Remove Tag',
46374     
46375             listeners : {
46376                 click : function ()
46377                 {
46378                     // remove
46379                     // undo does not work.
46380                      
46381                     var sn = tb.selectedNode;
46382                     
46383                     var pn = sn.parentNode;
46384                     
46385                     var stn =  sn.childNodes[0];
46386                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46387                     while (sn.childNodes.length) {
46388                         var node = sn.childNodes[0];
46389                         sn.removeChild(node);
46390                         //Roo.log(node);
46391                         pn.insertBefore(node, sn);
46392                         
46393                     }
46394                     pn.removeChild(sn);
46395                     var range = editorcore.createRange();
46396         
46397                     range.setStart(stn,0);
46398                     range.setEnd(en,0); //????
46399                     //range.selectNode(sel);
46400                     
46401                     
46402                     var selection = editorcore.getSelection();
46403                     selection.removeAllRanges();
46404                     selection.addRange(range);
46405                     
46406                     
46407                     
46408                     //_this.updateToolbar(null, null, pn);
46409                     _this.updateToolbar(null, null, null);
46410                     _this.footDisp.dom.innerHTML = ''; 
46411                 }
46412             }
46413             
46414                     
46415                 
46416             
46417         });
46418         
46419         
46420         tb.el.on('click', function(e){
46421             e.preventDefault(); // what does this do?
46422         });
46423         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46424         tb.el.hide();
46425         tb.name = nm;
46426         // dont need to disable them... as they will get hidden
46427         return tb;
46428          
46429         
46430     },
46431     buildFooter : function()
46432     {
46433         
46434         var fel = this.editor.wrap.createChild();
46435         this.footer = new Roo.Toolbar(fel);
46436         // toolbar has scrolly on left / right?
46437         var footDisp= new Roo.Toolbar.Fill();
46438         var _t = this;
46439         this.footer.add(
46440             {
46441                 text : '&lt;',
46442                 xtype: 'Button',
46443                 handler : function() {
46444                     _t.footDisp.scrollTo('left',0,true)
46445                 }
46446             }
46447         );
46448         this.footer.add( footDisp );
46449         this.footer.add( 
46450             {
46451                 text : '&gt;',
46452                 xtype: 'Button',
46453                 handler : function() {
46454                     // no animation..
46455                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46456                 }
46457             }
46458         );
46459         var fel = Roo.get(footDisp.el);
46460         fel.addClass('x-editor-context');
46461         this.footDispWrap = fel; 
46462         this.footDispWrap.overflow  = 'hidden';
46463         
46464         this.footDisp = fel.createChild();
46465         this.footDispWrap.on('click', this.onContextClick, this)
46466         
46467         
46468     },
46469     onContextClick : function (ev,dom)
46470     {
46471         ev.preventDefault();
46472         var  cn = dom.className;
46473         //Roo.log(cn);
46474         if (!cn.match(/x-ed-loc-/)) {
46475             return;
46476         }
46477         var n = cn.split('-').pop();
46478         var ans = this.footerEls;
46479         var sel = ans[n];
46480         
46481          // pick
46482         var range = this.editorcore.createRange();
46483         
46484         range.selectNodeContents(sel);
46485         //range.selectNode(sel);
46486         
46487         
46488         var selection = this.editorcore.getSelection();
46489         selection.removeAllRanges();
46490         selection.addRange(range);
46491         
46492         
46493         
46494         this.updateToolbar(null, null, sel);
46495         
46496         
46497     }
46498     
46499     
46500     
46501     
46502     
46503 });
46504
46505
46506
46507
46508
46509 /*
46510  * Based on:
46511  * Ext JS Library 1.1.1
46512  * Copyright(c) 2006-2007, Ext JS, LLC.
46513  *
46514  * Originally Released Under LGPL - original licence link has changed is not relivant.
46515  *
46516  * Fork - LGPL
46517  * <script type="text/javascript">
46518  */
46519  
46520 /**
46521  * @class Roo.form.BasicForm
46522  * @extends Roo.util.Observable
46523  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46524  * @constructor
46525  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46526  * @param {Object} config Configuration options
46527  */
46528 Roo.form.BasicForm = function(el, config){
46529     this.allItems = [];
46530     this.childForms = [];
46531     Roo.apply(this, config);
46532     /*
46533      * The Roo.form.Field items in this form.
46534      * @type MixedCollection
46535      */
46536      
46537      
46538     this.items = new Roo.util.MixedCollection(false, function(o){
46539         return o.id || (o.id = Roo.id());
46540     });
46541     this.addEvents({
46542         /**
46543          * @event beforeaction
46544          * Fires before any action is performed. Return false to cancel the action.
46545          * @param {Form} this
46546          * @param {Action} action The action to be performed
46547          */
46548         beforeaction: true,
46549         /**
46550          * @event actionfailed
46551          * Fires when an action fails.
46552          * @param {Form} this
46553          * @param {Action} action The action that failed
46554          */
46555         actionfailed : true,
46556         /**
46557          * @event actioncomplete
46558          * Fires when an action is completed.
46559          * @param {Form} this
46560          * @param {Action} action The action that completed
46561          */
46562         actioncomplete : true
46563     });
46564     if(el){
46565         this.initEl(el);
46566     }
46567     Roo.form.BasicForm.superclass.constructor.call(this);
46568 };
46569
46570 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46571     /**
46572      * @cfg {String} method
46573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46574      */
46575     /**
46576      * @cfg {DataReader} reader
46577      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46578      * This is optional as there is built-in support for processing JSON.
46579      */
46580     /**
46581      * @cfg {DataReader} errorReader
46582      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46583      * This is completely optional as there is built-in support for processing JSON.
46584      */
46585     /**
46586      * @cfg {String} url
46587      * The URL to use for form actions if one isn't supplied in the action options.
46588      */
46589     /**
46590      * @cfg {Boolean} fileUpload
46591      * Set to true if this form is a file upload.
46592      */
46593      
46594     /**
46595      * @cfg {Object} baseParams
46596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46597      */
46598      /**
46599      
46600     /**
46601      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46602      */
46603     timeout: 30,
46604
46605     // private
46606     activeAction : null,
46607
46608     /**
46609      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46610      * or setValues() data instead of when the form was first created.
46611      */
46612     trackResetOnLoad : false,
46613     
46614     
46615     /**
46616      * childForms - used for multi-tab forms
46617      * @type {Array}
46618      */
46619     childForms : false,
46620     
46621     /**
46622      * allItems - full list of fields.
46623      * @type {Array}
46624      */
46625     allItems : false,
46626     
46627     /**
46628      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46629      * element by passing it or its id or mask the form itself by passing in true.
46630      * @type Mixed
46631      */
46632     waitMsgTarget : false,
46633
46634     // private
46635     initEl : function(el){
46636         this.el = Roo.get(el);
46637         this.id = this.el.id || Roo.id();
46638         this.el.on('submit', this.onSubmit, this);
46639         this.el.addClass('x-form');
46640     },
46641
46642     // private
46643     onSubmit : function(e){
46644         e.stopEvent();
46645     },
46646
46647     /**
46648      * Returns true if client-side validation on the form is successful.
46649      * @return Boolean
46650      */
46651     isValid : function(){
46652         var valid = true;
46653         this.items.each(function(f){
46654            if(!f.validate()){
46655                valid = false;
46656            }
46657         });
46658         return valid;
46659     },
46660
46661     /**
46662      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46663      * @return Boolean
46664      */
46665     isDirty : function(){
46666         var dirty = false;
46667         this.items.each(function(f){
46668            if(f.isDirty()){
46669                dirty = true;
46670                return false;
46671            }
46672         });
46673         return dirty;
46674     },
46675     
46676     /**
46677      * Returns true if any fields in this form have changed since their original load. (New version)
46678      * @return Boolean
46679      */
46680     
46681     hasChanged : function()
46682     {
46683         var dirty = false;
46684         this.items.each(function(f){
46685            if(f.hasChanged()){
46686                dirty = true;
46687                return false;
46688            }
46689         });
46690         return dirty;
46691         
46692     },
46693     /**
46694      * Resets all hasChanged to 'false' -
46695      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46696      * So hasChanged storage is only to be used for this purpose
46697      * @return Boolean
46698      */
46699     resetHasChanged : function()
46700     {
46701         this.items.each(function(f){
46702            f.resetHasChanged();
46703         });
46704         
46705     },
46706     
46707     
46708     /**
46709      * Performs a predefined action (submit or load) or custom actions you define on this form.
46710      * @param {String} actionName The name of the action type
46711      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46712      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46713      * accept other config options):
46714      * <pre>
46715 Property          Type             Description
46716 ----------------  ---------------  ----------------------------------------------------------------------------------
46717 url               String           The url for the action (defaults to the form's url)
46718 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46719 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46720 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46721                                    validate the form on the client (defaults to false)
46722      * </pre>
46723      * @return {BasicForm} this
46724      */
46725     doAction : function(action, options){
46726         if(typeof action == 'string'){
46727             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46728         }
46729         if(this.fireEvent('beforeaction', this, action) !== false){
46730             this.beforeAction(action);
46731             action.run.defer(100, action);
46732         }
46733         return this;
46734     },
46735
46736     /**
46737      * Shortcut to do a submit action.
46738      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46739      * @return {BasicForm} this
46740      */
46741     submit : function(options){
46742         this.doAction('submit', options);
46743         return this;
46744     },
46745
46746     /**
46747      * Shortcut to do a load action.
46748      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46749      * @return {BasicForm} this
46750      */
46751     load : function(options){
46752         this.doAction('load', options);
46753         return this;
46754     },
46755
46756     /**
46757      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46758      * @param {Record} record The record to edit
46759      * @return {BasicForm} this
46760      */
46761     updateRecord : function(record){
46762         record.beginEdit();
46763         var fs = record.fields;
46764         fs.each(function(f){
46765             var field = this.findField(f.name);
46766             if(field){
46767                 record.set(f.name, field.getValue());
46768             }
46769         }, this);
46770         record.endEdit();
46771         return this;
46772     },
46773
46774     /**
46775      * Loads an Roo.data.Record into this form.
46776      * @param {Record} record The record to load
46777      * @return {BasicForm} this
46778      */
46779     loadRecord : function(record){
46780         this.setValues(record.data);
46781         return this;
46782     },
46783
46784     // private
46785     beforeAction : function(action){
46786         var o = action.options;
46787         
46788        
46789         if(this.waitMsgTarget === true){
46790             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46791         }else if(this.waitMsgTarget){
46792             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46793             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46794         }else {
46795             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46796         }
46797          
46798     },
46799
46800     // private
46801     afterAction : function(action, success){
46802         this.activeAction = null;
46803         var o = action.options;
46804         
46805         if(this.waitMsgTarget === true){
46806             this.el.unmask();
46807         }else if(this.waitMsgTarget){
46808             this.waitMsgTarget.unmask();
46809         }else{
46810             Roo.MessageBox.updateProgress(1);
46811             Roo.MessageBox.hide();
46812         }
46813          
46814         if(success){
46815             if(o.reset){
46816                 this.reset();
46817             }
46818             Roo.callback(o.success, o.scope, [this, action]);
46819             this.fireEvent('actioncomplete', this, action);
46820             
46821         }else{
46822             
46823             // failure condition..
46824             // we have a scenario where updates need confirming.
46825             // eg. if a locking scenario exists..
46826             // we look for { errors : { needs_confirm : true }} in the response.
46827             if (
46828                 (typeof(action.result) != 'undefined')  &&
46829                 (typeof(action.result.errors) != 'undefined')  &&
46830                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46831            ){
46832                 var _t = this;
46833                 Roo.MessageBox.confirm(
46834                     "Change requires confirmation",
46835                     action.result.errorMsg,
46836                     function(r) {
46837                         if (r != 'yes') {
46838                             return;
46839                         }
46840                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46841                     }
46842                     
46843                 );
46844                 
46845                 
46846                 
46847                 return;
46848             }
46849             
46850             Roo.callback(o.failure, o.scope, [this, action]);
46851             // show an error message if no failed handler is set..
46852             if (!this.hasListener('actionfailed')) {
46853                 Roo.MessageBox.alert("Error",
46854                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46855                         action.result.errorMsg :
46856                         "Saving Failed, please check your entries or try again"
46857                 );
46858             }
46859             
46860             this.fireEvent('actionfailed', this, action);
46861         }
46862         
46863     },
46864
46865     /**
46866      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46867      * @param {String} id The value to search for
46868      * @return Field
46869      */
46870     findField : function(id){
46871         var field = this.items.get(id);
46872         if(!field){
46873             this.items.each(function(f){
46874                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46875                     field = f;
46876                     return false;
46877                 }
46878             });
46879         }
46880         return field || null;
46881     },
46882
46883     /**
46884      * Add a secondary form to this one, 
46885      * Used to provide tabbed forms. One form is primary, with hidden values 
46886      * which mirror the elements from the other forms.
46887      * 
46888      * @param {Roo.form.Form} form to add.
46889      * 
46890      */
46891     addForm : function(form)
46892     {
46893        
46894         if (this.childForms.indexOf(form) > -1) {
46895             // already added..
46896             return;
46897         }
46898         this.childForms.push(form);
46899         var n = '';
46900         Roo.each(form.allItems, function (fe) {
46901             
46902             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46903             if (this.findField(n)) { // already added..
46904                 return;
46905             }
46906             var add = new Roo.form.Hidden({
46907                 name : n
46908             });
46909             add.render(this.el);
46910             
46911             this.add( add );
46912         }, this);
46913         
46914     },
46915     /**
46916      * Mark fields in this form invalid in bulk.
46917      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46918      * @return {BasicForm} this
46919      */
46920     markInvalid : function(errors){
46921         if(errors instanceof Array){
46922             for(var i = 0, len = errors.length; i < len; i++){
46923                 var fieldError = errors[i];
46924                 var f = this.findField(fieldError.id);
46925                 if(f){
46926                     f.markInvalid(fieldError.msg);
46927                 }
46928             }
46929         }else{
46930             var field, id;
46931             for(id in errors){
46932                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46933                     field.markInvalid(errors[id]);
46934                 }
46935             }
46936         }
46937         Roo.each(this.childForms || [], function (f) {
46938             f.markInvalid(errors);
46939         });
46940         
46941         return this;
46942     },
46943
46944     /**
46945      * Set values for fields in this form in bulk.
46946      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46947      * @return {BasicForm} this
46948      */
46949     setValues : function(values){
46950         if(values instanceof Array){ // array of objects
46951             for(var i = 0, len = values.length; i < len; i++){
46952                 var v = values[i];
46953                 var f = this.findField(v.id);
46954                 if(f){
46955                     f.setValue(v.value);
46956                     if(this.trackResetOnLoad){
46957                         f.originalValue = f.getValue();
46958                     }
46959                 }
46960             }
46961         }else{ // object hash
46962             var field, id;
46963             for(id in values){
46964                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46965                     
46966                     if (field.setFromData && 
46967                         field.valueField && 
46968                         field.displayField &&
46969                         // combos' with local stores can 
46970                         // be queried via setValue()
46971                         // to set their value..
46972                         (field.store && !field.store.isLocal)
46973                         ) {
46974                         // it's a combo
46975                         var sd = { };
46976                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46977                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46978                         field.setFromData(sd);
46979                         
46980                     } else {
46981                         field.setValue(values[id]);
46982                     }
46983                     
46984                     
46985                     if(this.trackResetOnLoad){
46986                         field.originalValue = field.getValue();
46987                     }
46988                 }
46989             }
46990         }
46991         this.resetHasChanged();
46992         
46993         
46994         Roo.each(this.childForms || [], function (f) {
46995             f.setValues(values);
46996             f.resetHasChanged();
46997         });
46998                 
46999         return this;
47000     },
47001
47002     /**
47003      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47004      * they are returned as an array.
47005      * @param {Boolean} asString
47006      * @return {Object}
47007      */
47008     getValues : function(asString){
47009         if (this.childForms) {
47010             // copy values from the child forms
47011             Roo.each(this.childForms, function (f) {
47012                 this.setValues(f.getValues());
47013             }, this);
47014         }
47015         
47016         
47017         
47018         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47019         if(asString === true){
47020             return fs;
47021         }
47022         return Roo.urlDecode(fs);
47023     },
47024     
47025     /**
47026      * Returns the fields in this form as an object with key/value pairs. 
47027      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47028      * @return {Object}
47029      */
47030     getFieldValues : function(with_hidden)
47031     {
47032         if (this.childForms) {
47033             // copy values from the child forms
47034             // should this call getFieldValues - probably not as we do not currently copy
47035             // hidden fields when we generate..
47036             Roo.each(this.childForms, function (f) {
47037                 this.setValues(f.getValues());
47038             }, this);
47039         }
47040         
47041         var ret = {};
47042         this.items.each(function(f){
47043             if (!f.getName()) {
47044                 return;
47045             }
47046             var v = f.getValue();
47047             if (f.inputType =='radio') {
47048                 if (typeof(ret[f.getName()]) == 'undefined') {
47049                     ret[f.getName()] = ''; // empty..
47050                 }
47051                 
47052                 if (!f.el.dom.checked) {
47053                     return;
47054                     
47055                 }
47056                 v = f.el.dom.value;
47057                 
47058             }
47059             
47060             // not sure if this supported any more..
47061             if ((typeof(v) == 'object') && f.getRawValue) {
47062                 v = f.getRawValue() ; // dates..
47063             }
47064             // combo boxes where name != hiddenName...
47065             if (f.name != f.getName()) {
47066                 ret[f.name] = f.getRawValue();
47067             }
47068             ret[f.getName()] = v;
47069         });
47070         
47071         return ret;
47072     },
47073
47074     /**
47075      * Clears all invalid messages in this form.
47076      * @return {BasicForm} this
47077      */
47078     clearInvalid : function(){
47079         this.items.each(function(f){
47080            f.clearInvalid();
47081         });
47082         
47083         Roo.each(this.childForms || [], function (f) {
47084             f.clearInvalid();
47085         });
47086         
47087         
47088         return this;
47089     },
47090
47091     /**
47092      * Resets this form.
47093      * @return {BasicForm} this
47094      */
47095     reset : function(){
47096         this.items.each(function(f){
47097             f.reset();
47098         });
47099         
47100         Roo.each(this.childForms || [], function (f) {
47101             f.reset();
47102         });
47103         this.resetHasChanged();
47104         
47105         return this;
47106     },
47107
47108     /**
47109      * Add Roo.form components to this form.
47110      * @param {Field} field1
47111      * @param {Field} field2 (optional)
47112      * @param {Field} etc (optional)
47113      * @return {BasicForm} this
47114      */
47115     add : function(){
47116         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47117         return this;
47118     },
47119
47120
47121     /**
47122      * Removes a field from the items collection (does NOT remove its markup).
47123      * @param {Field} field
47124      * @return {BasicForm} this
47125      */
47126     remove : function(field){
47127         this.items.remove(field);
47128         return this;
47129     },
47130
47131     /**
47132      * Looks at the fields in this form, checks them for an id attribute,
47133      * and calls applyTo on the existing dom element with that id.
47134      * @return {BasicForm} this
47135      */
47136     render : function(){
47137         this.items.each(function(f){
47138             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47139                 f.applyTo(f.id);
47140             }
47141         });
47142         return this;
47143     },
47144
47145     /**
47146      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47147      * @param {Object} values
47148      * @return {BasicForm} this
47149      */
47150     applyToFields : function(o){
47151         this.items.each(function(f){
47152            Roo.apply(f, o);
47153         });
47154         return this;
47155     },
47156
47157     /**
47158      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47159      * @param {Object} values
47160      * @return {BasicForm} this
47161      */
47162     applyIfToFields : function(o){
47163         this.items.each(function(f){
47164            Roo.applyIf(f, o);
47165         });
47166         return this;
47167     }
47168 });
47169
47170 // back compat
47171 Roo.BasicForm = Roo.form.BasicForm;/*
47172  * Based on:
47173  * Ext JS Library 1.1.1
47174  * Copyright(c) 2006-2007, Ext JS, LLC.
47175  *
47176  * Originally Released Under LGPL - original licence link has changed is not relivant.
47177  *
47178  * Fork - LGPL
47179  * <script type="text/javascript">
47180  */
47181
47182 /**
47183  * @class Roo.form.Form
47184  * @extends Roo.form.BasicForm
47185  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47186  * @constructor
47187  * @param {Object} config Configuration options
47188  */
47189 Roo.form.Form = function(config){
47190     var xitems =  [];
47191     if (config.items) {
47192         xitems = config.items;
47193         delete config.items;
47194     }
47195    
47196     
47197     Roo.form.Form.superclass.constructor.call(this, null, config);
47198     this.url = this.url || this.action;
47199     if(!this.root){
47200         this.root = new Roo.form.Layout(Roo.applyIf({
47201             id: Roo.id()
47202         }, config));
47203     }
47204     this.active = this.root;
47205     /**
47206      * Array of all the buttons that have been added to this form via {@link addButton}
47207      * @type Array
47208      */
47209     this.buttons = [];
47210     this.allItems = [];
47211     this.addEvents({
47212         /**
47213          * @event clientvalidation
47214          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47215          * @param {Form} this
47216          * @param {Boolean} valid true if the form has passed client-side validation
47217          */
47218         clientvalidation: true,
47219         /**
47220          * @event rendered
47221          * Fires when the form is rendered
47222          * @param {Roo.form.Form} form
47223          */
47224         rendered : true
47225     });
47226     
47227     if (this.progressUrl) {
47228             // push a hidden field onto the list of fields..
47229             this.addxtype( {
47230                     xns: Roo.form, 
47231                     xtype : 'Hidden', 
47232                     name : 'UPLOAD_IDENTIFIER' 
47233             });
47234         }
47235         
47236     
47237     Roo.each(xitems, this.addxtype, this);
47238     
47239     
47240     
47241 };
47242
47243 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47244     /**
47245      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47246      */
47247     /**
47248      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47249      */
47250     /**
47251      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47252      */
47253     buttonAlign:'center',
47254
47255     /**
47256      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47257      */
47258     minButtonWidth:75,
47259
47260     /**
47261      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47262      * This property cascades to child containers if not set.
47263      */
47264     labelAlign:'left',
47265
47266     /**
47267      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47268      * fires a looping event with that state. This is required to bind buttons to the valid
47269      * state using the config value formBind:true on the button.
47270      */
47271     monitorValid : false,
47272
47273     /**
47274      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47275      */
47276     monitorPoll : 200,
47277     
47278     /**
47279      * @cfg {String} progressUrl - Url to return progress data 
47280      */
47281     
47282     progressUrl : false,
47283   
47284     /**
47285      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47286      * fields are added and the column is closed. If no fields are passed the column remains open
47287      * until end() is called.
47288      * @param {Object} config The config to pass to the column
47289      * @param {Field} field1 (optional)
47290      * @param {Field} field2 (optional)
47291      * @param {Field} etc (optional)
47292      * @return Column The column container object
47293      */
47294     column : function(c){
47295         var col = new Roo.form.Column(c);
47296         this.start(col);
47297         if(arguments.length > 1){ // duplicate code required because of Opera
47298             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47299             this.end();
47300         }
47301         return col;
47302     },
47303
47304     /**
47305      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47306      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47307      * until end() is called.
47308      * @param {Object} config The config to pass to the fieldset
47309      * @param {Field} field1 (optional)
47310      * @param {Field} field2 (optional)
47311      * @param {Field} etc (optional)
47312      * @return FieldSet The fieldset container object
47313      */
47314     fieldset : function(c){
47315         var fs = new Roo.form.FieldSet(c);
47316         this.start(fs);
47317         if(arguments.length > 1){ // duplicate code required because of Opera
47318             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47319             this.end();
47320         }
47321         return fs;
47322     },
47323
47324     /**
47325      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47326      * fields are added and the container is closed. If no fields are passed the container remains open
47327      * until end() is called.
47328      * @param {Object} config The config to pass to the Layout
47329      * @param {Field} field1 (optional)
47330      * @param {Field} field2 (optional)
47331      * @param {Field} etc (optional)
47332      * @return Layout The container object
47333      */
47334     container : function(c){
47335         var l = new Roo.form.Layout(c);
47336         this.start(l);
47337         if(arguments.length > 1){ // duplicate code required because of Opera
47338             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47339             this.end();
47340         }
47341         return l;
47342     },
47343
47344     /**
47345      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47346      * @param {Object} container A Roo.form.Layout or subclass of Layout
47347      * @return {Form} this
47348      */
47349     start : function(c){
47350         // cascade label info
47351         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47352         this.active.stack.push(c);
47353         c.ownerCt = this.active;
47354         this.active = c;
47355         return this;
47356     },
47357
47358     /**
47359      * Closes the current open container
47360      * @return {Form} this
47361      */
47362     end : function(){
47363         if(this.active == this.root){
47364             return this;
47365         }
47366         this.active = this.active.ownerCt;
47367         return this;
47368     },
47369
47370     /**
47371      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47372      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47373      * as the label of the field.
47374      * @param {Field} field1
47375      * @param {Field} field2 (optional)
47376      * @param {Field} etc. (optional)
47377      * @return {Form} this
47378      */
47379     add : function(){
47380         this.active.stack.push.apply(this.active.stack, arguments);
47381         this.allItems.push.apply(this.allItems,arguments);
47382         var r = [];
47383         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47384             if(a[i].isFormField){
47385                 r.push(a[i]);
47386             }
47387         }
47388         if(r.length > 0){
47389             Roo.form.Form.superclass.add.apply(this, r);
47390         }
47391         return this;
47392     },
47393     
47394
47395     
47396     
47397     
47398      /**
47399      * Find any element that has been added to a form, using it's ID or name
47400      * This can include framesets, columns etc. along with regular fields..
47401      * @param {String} id - id or name to find.
47402      
47403      * @return {Element} e - or false if nothing found.
47404      */
47405     findbyId : function(id)
47406     {
47407         var ret = false;
47408         if (!id) {
47409             return ret;
47410         }
47411         Roo.each(this.allItems, function(f){
47412             if (f.id == id || f.name == id ){
47413                 ret = f;
47414                 return false;
47415             }
47416         });
47417         return ret;
47418     },
47419
47420     
47421     
47422     /**
47423      * Render this form into the passed container. This should only be called once!
47424      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47425      * @return {Form} this
47426      */
47427     render : function(ct)
47428     {
47429         
47430         
47431         
47432         ct = Roo.get(ct);
47433         var o = this.autoCreate || {
47434             tag: 'form',
47435             method : this.method || 'POST',
47436             id : this.id || Roo.id()
47437         };
47438         this.initEl(ct.createChild(o));
47439
47440         this.root.render(this.el);
47441         
47442        
47443              
47444         this.items.each(function(f){
47445             f.render('x-form-el-'+f.id);
47446         });
47447
47448         if(this.buttons.length > 0){
47449             // tables are required to maintain order and for correct IE layout
47450             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47451                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47452                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47453             }}, null, true);
47454             var tr = tb.getElementsByTagName('tr')[0];
47455             for(var i = 0, len = this.buttons.length; i < len; i++) {
47456                 var b = this.buttons[i];
47457                 var td = document.createElement('td');
47458                 td.className = 'x-form-btn-td';
47459                 b.render(tr.appendChild(td));
47460             }
47461         }
47462         if(this.monitorValid){ // initialize after render
47463             this.startMonitoring();
47464         }
47465         this.fireEvent('rendered', this);
47466         return this;
47467     },
47468
47469     /**
47470      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47471      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47472      * object or a valid Roo.DomHelper element config
47473      * @param {Function} handler The function called when the button is clicked
47474      * @param {Object} scope (optional) The scope of the handler function
47475      * @return {Roo.Button}
47476      */
47477     addButton : function(config, handler, scope){
47478         var bc = {
47479             handler: handler,
47480             scope: scope,
47481             minWidth: this.minButtonWidth,
47482             hideParent:true
47483         };
47484         if(typeof config == "string"){
47485             bc.text = config;
47486         }else{
47487             Roo.apply(bc, config);
47488         }
47489         var btn = new Roo.Button(null, bc);
47490         this.buttons.push(btn);
47491         return btn;
47492     },
47493
47494      /**
47495      * Adds a series of form elements (using the xtype property as the factory method.
47496      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47497      * @param {Object} config 
47498      */
47499     
47500     addxtype : function()
47501     {
47502         var ar = Array.prototype.slice.call(arguments, 0);
47503         var ret = false;
47504         for(var i = 0; i < ar.length; i++) {
47505             if (!ar[i]) {
47506                 continue; // skip -- if this happends something invalid got sent, we 
47507                 // should ignore it, as basically that interface element will not show up
47508                 // and that should be pretty obvious!!
47509             }
47510             
47511             if (Roo.form[ar[i].xtype]) {
47512                 ar[i].form = this;
47513                 var fe = Roo.factory(ar[i], Roo.form);
47514                 if (!ret) {
47515                     ret = fe;
47516                 }
47517                 fe.form = this;
47518                 if (fe.store) {
47519                     fe.store.form = this;
47520                 }
47521                 if (fe.isLayout) {  
47522                          
47523                     this.start(fe);
47524                     this.allItems.push(fe);
47525                     if (fe.items && fe.addxtype) {
47526                         fe.addxtype.apply(fe, fe.items);
47527                         delete fe.items;
47528                     }
47529                      this.end();
47530                     continue;
47531                 }
47532                 
47533                 
47534                  
47535                 this.add(fe);
47536               //  console.log('adding ' + ar[i].xtype);
47537             }
47538             if (ar[i].xtype == 'Button') {  
47539                 //console.log('adding button');
47540                 //console.log(ar[i]);
47541                 this.addButton(ar[i]);
47542                 this.allItems.push(fe);
47543                 continue;
47544             }
47545             
47546             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47547                 alert('end is not supported on xtype any more, use items');
47548             //    this.end();
47549             //    //console.log('adding end');
47550             }
47551             
47552         }
47553         return ret;
47554     },
47555     
47556     /**
47557      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47558      * option "monitorValid"
47559      */
47560     startMonitoring : function(){
47561         if(!this.bound){
47562             this.bound = true;
47563             Roo.TaskMgr.start({
47564                 run : this.bindHandler,
47565                 interval : this.monitorPoll || 200,
47566                 scope: this
47567             });
47568         }
47569     },
47570
47571     /**
47572      * Stops monitoring of the valid state of this form
47573      */
47574     stopMonitoring : function(){
47575         this.bound = false;
47576     },
47577
47578     // private
47579     bindHandler : function(){
47580         if(!this.bound){
47581             return false; // stops binding
47582         }
47583         var valid = true;
47584         this.items.each(function(f){
47585             if(!f.isValid(true)){
47586                 valid = false;
47587                 return false;
47588             }
47589         });
47590         for(var i = 0, len = this.buttons.length; i < len; i++){
47591             var btn = this.buttons[i];
47592             if(btn.formBind === true && btn.disabled === valid){
47593                 btn.setDisabled(!valid);
47594             }
47595         }
47596         this.fireEvent('clientvalidation', this, valid);
47597     }
47598     
47599     
47600     
47601     
47602     
47603     
47604     
47605     
47606 });
47607
47608
47609 // back compat
47610 Roo.Form = Roo.form.Form;
47611 /*
47612  * Based on:
47613  * Ext JS Library 1.1.1
47614  * Copyright(c) 2006-2007, Ext JS, LLC.
47615  *
47616  * Originally Released Under LGPL - original licence link has changed is not relivant.
47617  *
47618  * Fork - LGPL
47619  * <script type="text/javascript">
47620  */
47621
47622 // as we use this in bootstrap.
47623 Roo.namespace('Roo.form');
47624  /**
47625  * @class Roo.form.Action
47626  * Internal Class used to handle form actions
47627  * @constructor
47628  * @param {Roo.form.BasicForm} el The form element or its id
47629  * @param {Object} config Configuration options
47630  */
47631
47632  
47633  
47634 // define the action interface
47635 Roo.form.Action = function(form, options){
47636     this.form = form;
47637     this.options = options || {};
47638 };
47639 /**
47640  * Client Validation Failed
47641  * @const 
47642  */
47643 Roo.form.Action.CLIENT_INVALID = 'client';
47644 /**
47645  * Server Validation Failed
47646  * @const 
47647  */
47648 Roo.form.Action.SERVER_INVALID = 'server';
47649  /**
47650  * Connect to Server Failed
47651  * @const 
47652  */
47653 Roo.form.Action.CONNECT_FAILURE = 'connect';
47654 /**
47655  * Reading Data from Server Failed
47656  * @const 
47657  */
47658 Roo.form.Action.LOAD_FAILURE = 'load';
47659
47660 Roo.form.Action.prototype = {
47661     type : 'default',
47662     failureType : undefined,
47663     response : undefined,
47664     result : undefined,
47665
47666     // interface method
47667     run : function(options){
47668
47669     },
47670
47671     // interface method
47672     success : function(response){
47673
47674     },
47675
47676     // interface method
47677     handleResponse : function(response){
47678
47679     },
47680
47681     // default connection failure
47682     failure : function(response){
47683         
47684         this.response = response;
47685         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47686         this.form.afterAction(this, false);
47687     },
47688
47689     processResponse : function(response){
47690         this.response = response;
47691         if(!response.responseText){
47692             return true;
47693         }
47694         this.result = this.handleResponse(response);
47695         return this.result;
47696     },
47697
47698     // utility functions used internally
47699     getUrl : function(appendParams){
47700         var url = this.options.url || this.form.url || this.form.el.dom.action;
47701         if(appendParams){
47702             var p = this.getParams();
47703             if(p){
47704                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47705             }
47706         }
47707         return url;
47708     },
47709
47710     getMethod : function(){
47711         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47712     },
47713
47714     getParams : function(){
47715         var bp = this.form.baseParams;
47716         var p = this.options.params;
47717         if(p){
47718             if(typeof p == "object"){
47719                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47720             }else if(typeof p == 'string' && bp){
47721                 p += '&' + Roo.urlEncode(bp);
47722             }
47723         }else if(bp){
47724             p = Roo.urlEncode(bp);
47725         }
47726         return p;
47727     },
47728
47729     createCallback : function(){
47730         return {
47731             success: this.success,
47732             failure: this.failure,
47733             scope: this,
47734             timeout: (this.form.timeout*1000),
47735             upload: this.form.fileUpload ? this.success : undefined
47736         };
47737     }
47738 };
47739
47740 Roo.form.Action.Submit = function(form, options){
47741     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47742 };
47743
47744 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47745     type : 'submit',
47746
47747     haveProgress : false,
47748     uploadComplete : false,
47749     
47750     // uploadProgress indicator.
47751     uploadProgress : function()
47752     {
47753         if (!this.form.progressUrl) {
47754             return;
47755         }
47756         
47757         if (!this.haveProgress) {
47758             Roo.MessageBox.progress("Uploading", "Uploading");
47759         }
47760         if (this.uploadComplete) {
47761            Roo.MessageBox.hide();
47762            return;
47763         }
47764         
47765         this.haveProgress = true;
47766    
47767         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47768         
47769         var c = new Roo.data.Connection();
47770         c.request({
47771             url : this.form.progressUrl,
47772             params: {
47773                 id : uid
47774             },
47775             method: 'GET',
47776             success : function(req){
47777                //console.log(data);
47778                 var rdata = false;
47779                 var edata;
47780                 try  {
47781                    rdata = Roo.decode(req.responseText)
47782                 } catch (e) {
47783                     Roo.log("Invalid data from server..");
47784                     Roo.log(edata);
47785                     return;
47786                 }
47787                 if (!rdata || !rdata.success) {
47788                     Roo.log(rdata);
47789                     Roo.MessageBox.alert(Roo.encode(rdata));
47790                     return;
47791                 }
47792                 var data = rdata.data;
47793                 
47794                 if (this.uploadComplete) {
47795                    Roo.MessageBox.hide();
47796                    return;
47797                 }
47798                    
47799                 if (data){
47800                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47801                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47802                     );
47803                 }
47804                 this.uploadProgress.defer(2000,this);
47805             },
47806        
47807             failure: function(data) {
47808                 Roo.log('progress url failed ');
47809                 Roo.log(data);
47810             },
47811             scope : this
47812         });
47813            
47814     },
47815     
47816     
47817     run : function()
47818     {
47819         // run get Values on the form, so it syncs any secondary forms.
47820         this.form.getValues();
47821         
47822         var o = this.options;
47823         var method = this.getMethod();
47824         var isPost = method == 'POST';
47825         if(o.clientValidation === false || this.form.isValid()){
47826             
47827             if (this.form.progressUrl) {
47828                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47829                     (new Date() * 1) + '' + Math.random());
47830                     
47831             } 
47832             
47833             
47834             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47835                 form:this.form.el.dom,
47836                 url:this.getUrl(!isPost),
47837                 method: method,
47838                 params:isPost ? this.getParams() : null,
47839                 isUpload: this.form.fileUpload
47840             }));
47841             
47842             this.uploadProgress();
47843
47844         }else if (o.clientValidation !== false){ // client validation failed
47845             this.failureType = Roo.form.Action.CLIENT_INVALID;
47846             this.form.afterAction(this, false);
47847         }
47848     },
47849
47850     success : function(response)
47851     {
47852         this.uploadComplete= true;
47853         if (this.haveProgress) {
47854             Roo.MessageBox.hide();
47855         }
47856         
47857         
47858         var result = this.processResponse(response);
47859         if(result === true || result.success){
47860             this.form.afterAction(this, true);
47861             return;
47862         }
47863         if(result.errors){
47864             this.form.markInvalid(result.errors);
47865             this.failureType = Roo.form.Action.SERVER_INVALID;
47866         }
47867         this.form.afterAction(this, false);
47868     },
47869     failure : function(response)
47870     {
47871         this.uploadComplete= true;
47872         if (this.haveProgress) {
47873             Roo.MessageBox.hide();
47874         }
47875         
47876         this.response = response;
47877         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47878         this.form.afterAction(this, false);
47879     },
47880     
47881     handleResponse : function(response){
47882         if(this.form.errorReader){
47883             var rs = this.form.errorReader.read(response);
47884             var errors = [];
47885             if(rs.records){
47886                 for(var i = 0, len = rs.records.length; i < len; i++) {
47887                     var r = rs.records[i];
47888                     errors[i] = r.data;
47889                 }
47890             }
47891             if(errors.length < 1){
47892                 errors = null;
47893             }
47894             return {
47895                 success : rs.success,
47896                 errors : errors
47897             };
47898         }
47899         var ret = false;
47900         try {
47901             ret = Roo.decode(response.responseText);
47902         } catch (e) {
47903             ret = {
47904                 success: false,
47905                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47906                 errors : []
47907             };
47908         }
47909         return ret;
47910         
47911     }
47912 });
47913
47914
47915 Roo.form.Action.Load = function(form, options){
47916     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47917     this.reader = this.form.reader;
47918 };
47919
47920 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47921     type : 'load',
47922
47923     run : function(){
47924         
47925         Roo.Ajax.request(Roo.apply(
47926                 this.createCallback(), {
47927                     method:this.getMethod(),
47928                     url:this.getUrl(false),
47929                     params:this.getParams()
47930         }));
47931     },
47932
47933     success : function(response){
47934         
47935         var result = this.processResponse(response);
47936         if(result === true || !result.success || !result.data){
47937             this.failureType = Roo.form.Action.LOAD_FAILURE;
47938             this.form.afterAction(this, false);
47939             return;
47940         }
47941         this.form.clearInvalid();
47942         this.form.setValues(result.data);
47943         this.form.afterAction(this, true);
47944     },
47945
47946     handleResponse : function(response){
47947         if(this.form.reader){
47948             var rs = this.form.reader.read(response);
47949             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47950             return {
47951                 success : rs.success,
47952                 data : data
47953             };
47954         }
47955         return Roo.decode(response.responseText);
47956     }
47957 });
47958
47959 Roo.form.Action.ACTION_TYPES = {
47960     'load' : Roo.form.Action.Load,
47961     'submit' : Roo.form.Action.Submit
47962 };/*
47963  * Based on:
47964  * Ext JS Library 1.1.1
47965  * Copyright(c) 2006-2007, Ext JS, LLC.
47966  *
47967  * Originally Released Under LGPL - original licence link has changed is not relivant.
47968  *
47969  * Fork - LGPL
47970  * <script type="text/javascript">
47971  */
47972  
47973 /**
47974  * @class Roo.form.Layout
47975  * @extends Roo.Component
47976  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47977  * @constructor
47978  * @param {Object} config Configuration options
47979  */
47980 Roo.form.Layout = function(config){
47981     var xitems = [];
47982     if (config.items) {
47983         xitems = config.items;
47984         delete config.items;
47985     }
47986     Roo.form.Layout.superclass.constructor.call(this, config);
47987     this.stack = [];
47988     Roo.each(xitems, this.addxtype, this);
47989      
47990 };
47991
47992 Roo.extend(Roo.form.Layout, Roo.Component, {
47993     /**
47994      * @cfg {String/Object} autoCreate
47995      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
47996      */
47997     /**
47998      * @cfg {String/Object/Function} style
47999      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48000      * a function which returns such a specification.
48001      */
48002     /**
48003      * @cfg {String} labelAlign
48004      * Valid values are "left," "top" and "right" (defaults to "left")
48005      */
48006     /**
48007      * @cfg {Number} labelWidth
48008      * Fixed width in pixels of all field labels (defaults to undefined)
48009      */
48010     /**
48011      * @cfg {Boolean} clear
48012      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48013      */
48014     clear : true,
48015     /**
48016      * @cfg {String} labelSeparator
48017      * The separator to use after field labels (defaults to ':')
48018      */
48019     labelSeparator : ':',
48020     /**
48021      * @cfg {Boolean} hideLabels
48022      * True to suppress the display of field labels in this layout (defaults to false)
48023      */
48024     hideLabels : false,
48025
48026     // private
48027     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48028     
48029     isLayout : true,
48030     
48031     // private
48032     onRender : function(ct, position){
48033         if(this.el){ // from markup
48034             this.el = Roo.get(this.el);
48035         }else {  // generate
48036             var cfg = this.getAutoCreate();
48037             this.el = ct.createChild(cfg, position);
48038         }
48039         if(this.style){
48040             this.el.applyStyles(this.style);
48041         }
48042         if(this.labelAlign){
48043             this.el.addClass('x-form-label-'+this.labelAlign);
48044         }
48045         if(this.hideLabels){
48046             this.labelStyle = "display:none";
48047             this.elementStyle = "padding-left:0;";
48048         }else{
48049             if(typeof this.labelWidth == 'number'){
48050                 this.labelStyle = "width:"+this.labelWidth+"px;";
48051                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48052             }
48053             if(this.labelAlign == 'top'){
48054                 this.labelStyle = "width:auto;";
48055                 this.elementStyle = "padding-left:0;";
48056             }
48057         }
48058         var stack = this.stack;
48059         var slen = stack.length;
48060         if(slen > 0){
48061             if(!this.fieldTpl){
48062                 var t = new Roo.Template(
48063                     '<div class="x-form-item {5}">',
48064                         '<label for="{0}" style="{2}">{1}{4}</label>',
48065                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48066                         '</div>',
48067                     '</div><div class="x-form-clear-left"></div>'
48068                 );
48069                 t.disableFormats = true;
48070                 t.compile();
48071                 Roo.form.Layout.prototype.fieldTpl = t;
48072             }
48073             for(var i = 0; i < slen; i++) {
48074                 if(stack[i].isFormField){
48075                     this.renderField(stack[i]);
48076                 }else{
48077                     this.renderComponent(stack[i]);
48078                 }
48079             }
48080         }
48081         if(this.clear){
48082             this.el.createChild({cls:'x-form-clear'});
48083         }
48084     },
48085
48086     // private
48087     renderField : function(f){
48088         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48089                f.id, //0
48090                f.fieldLabel, //1
48091                f.labelStyle||this.labelStyle||'', //2
48092                this.elementStyle||'', //3
48093                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48094                f.itemCls||this.itemCls||''  //5
48095        ], true).getPrevSibling());
48096     },
48097
48098     // private
48099     renderComponent : function(c){
48100         c.render(c.isLayout ? this.el : this.el.createChild());    
48101     },
48102     /**
48103      * Adds a object form elements (using the xtype property as the factory method.)
48104      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48105      * @param {Object} config 
48106      */
48107     addxtype : function(o)
48108     {
48109         // create the lement.
48110         o.form = this.form;
48111         var fe = Roo.factory(o, Roo.form);
48112         this.form.allItems.push(fe);
48113         this.stack.push(fe);
48114         
48115         if (fe.isFormField) {
48116             this.form.items.add(fe);
48117         }
48118          
48119         return fe;
48120     }
48121 });
48122
48123 /**
48124  * @class Roo.form.Column
48125  * @extends Roo.form.Layout
48126  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48127  * @constructor
48128  * @param {Object} config Configuration options
48129  */
48130 Roo.form.Column = function(config){
48131     Roo.form.Column.superclass.constructor.call(this, config);
48132 };
48133
48134 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48135     /**
48136      * @cfg {Number/String} width
48137      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48138      */
48139     /**
48140      * @cfg {String/Object} autoCreate
48141      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48142      */
48143
48144     // private
48145     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48146
48147     // private
48148     onRender : function(ct, position){
48149         Roo.form.Column.superclass.onRender.call(this, ct, position);
48150         if(this.width){
48151             this.el.setWidth(this.width);
48152         }
48153     }
48154 });
48155
48156
48157 /**
48158  * @class Roo.form.Row
48159  * @extends Roo.form.Layout
48160  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48161  * @constructor
48162  * @param {Object} config Configuration options
48163  */
48164
48165  
48166 Roo.form.Row = function(config){
48167     Roo.form.Row.superclass.constructor.call(this, config);
48168 };
48169  
48170 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48171       /**
48172      * @cfg {Number/String} width
48173      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48174      */
48175     /**
48176      * @cfg {Number/String} height
48177      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48178      */
48179     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48180     
48181     padWidth : 20,
48182     // private
48183     onRender : function(ct, position){
48184         //console.log('row render');
48185         if(!this.rowTpl){
48186             var t = new Roo.Template(
48187                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48188                     '<label for="{0}" style="{2}">{1}{4}</label>',
48189                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48190                     '</div>',
48191                 '</div>'
48192             );
48193             t.disableFormats = true;
48194             t.compile();
48195             Roo.form.Layout.prototype.rowTpl = t;
48196         }
48197         this.fieldTpl = this.rowTpl;
48198         
48199         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48200         var labelWidth = 100;
48201         
48202         if ((this.labelAlign != 'top')) {
48203             if (typeof this.labelWidth == 'number') {
48204                 labelWidth = this.labelWidth
48205             }
48206             this.padWidth =  20 + labelWidth;
48207             
48208         }
48209         
48210         Roo.form.Column.superclass.onRender.call(this, ct, position);
48211         if(this.width){
48212             this.el.setWidth(this.width);
48213         }
48214         if(this.height){
48215             this.el.setHeight(this.height);
48216         }
48217     },
48218     
48219     // private
48220     renderField : function(f){
48221         f.fieldEl = this.fieldTpl.append(this.el, [
48222                f.id, f.fieldLabel,
48223                f.labelStyle||this.labelStyle||'',
48224                this.elementStyle||'',
48225                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48226                f.itemCls||this.itemCls||'',
48227                f.width ? f.width + this.padWidth : 160 + this.padWidth
48228        ],true);
48229     }
48230 });
48231  
48232
48233 /**
48234  * @class Roo.form.FieldSet
48235  * @extends Roo.form.Layout
48236  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48237  * @constructor
48238  * @param {Object} config Configuration options
48239  */
48240 Roo.form.FieldSet = function(config){
48241     Roo.form.FieldSet.superclass.constructor.call(this, config);
48242 };
48243
48244 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48245     /**
48246      * @cfg {String} legend
48247      * The text to display as the legend for the FieldSet (defaults to '')
48248      */
48249     /**
48250      * @cfg {String/Object} autoCreate
48251      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48252      */
48253
48254     // private
48255     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48256
48257     // private
48258     onRender : function(ct, position){
48259         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48260         if(this.legend){
48261             this.setLegend(this.legend);
48262         }
48263     },
48264
48265     // private
48266     setLegend : function(text){
48267         if(this.rendered){
48268             this.el.child('legend').update(text);
48269         }
48270     }
48271 });/*
48272  * Based on:
48273  * Ext JS Library 1.1.1
48274  * Copyright(c) 2006-2007, Ext JS, LLC.
48275  *
48276  * Originally Released Under LGPL - original licence link has changed is not relivant.
48277  *
48278  * Fork - LGPL
48279  * <script type="text/javascript">
48280  */
48281 /**
48282  * @class Roo.form.VTypes
48283  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48284  * @singleton
48285  */
48286 Roo.form.VTypes = function(){
48287     // closure these in so they are only created once.
48288     var alpha = /^[a-zA-Z_]+$/;
48289     var alphanum = /^[a-zA-Z0-9_]+$/;
48290     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48291     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48292
48293     // All these messages and functions are configurable
48294     return {
48295         /**
48296          * The function used to validate email addresses
48297          * @param {String} value The email address
48298          */
48299         'email' : function(v){
48300             return email.test(v);
48301         },
48302         /**
48303          * The error text to display when the email validation function returns false
48304          * @type String
48305          */
48306         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48307         /**
48308          * The keystroke filter mask to be applied on email input
48309          * @type RegExp
48310          */
48311         'emailMask' : /[a-z0-9_\.\-@]/i,
48312
48313         /**
48314          * The function used to validate URLs
48315          * @param {String} value The URL
48316          */
48317         'url' : function(v){
48318             return url.test(v);
48319         },
48320         /**
48321          * The error text to display when the url validation function returns false
48322          * @type String
48323          */
48324         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48325         
48326         /**
48327          * The function used to validate alpha values
48328          * @param {String} value The value
48329          */
48330         'alpha' : function(v){
48331             return alpha.test(v);
48332         },
48333         /**
48334          * The error text to display when the alpha validation function returns false
48335          * @type String
48336          */
48337         'alphaText' : 'This field should only contain letters and _',
48338         /**
48339          * The keystroke filter mask to be applied on alpha input
48340          * @type RegExp
48341          */
48342         'alphaMask' : /[a-z_]/i,
48343
48344         /**
48345          * The function used to validate alphanumeric values
48346          * @param {String} value The value
48347          */
48348         'alphanum' : function(v){
48349             return alphanum.test(v);
48350         },
48351         /**
48352          * The error text to display when the alphanumeric validation function returns false
48353          * @type String
48354          */
48355         'alphanumText' : 'This field should only contain letters, numbers and _',
48356         /**
48357          * The keystroke filter mask to be applied on alphanumeric input
48358          * @type RegExp
48359          */
48360         'alphanumMask' : /[a-z0-9_]/i
48361     };
48362 }();//<script type="text/javascript">
48363
48364 /**
48365  * @class Roo.form.FCKeditor
48366  * @extends Roo.form.TextArea
48367  * Wrapper around the FCKEditor http://www.fckeditor.net
48368  * @constructor
48369  * Creates a new FCKeditor
48370  * @param {Object} config Configuration options
48371  */
48372 Roo.form.FCKeditor = function(config){
48373     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48374     this.addEvents({
48375          /**
48376          * @event editorinit
48377          * Fired when the editor is initialized - you can add extra handlers here..
48378          * @param {FCKeditor} this
48379          * @param {Object} the FCK object.
48380          */
48381         editorinit : true
48382     });
48383     
48384     
48385 };
48386 Roo.form.FCKeditor.editors = { };
48387 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48388 {
48389     //defaultAutoCreate : {
48390     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48391     //},
48392     // private
48393     /**
48394      * @cfg {Object} fck options - see fck manual for details.
48395      */
48396     fckconfig : false,
48397     
48398     /**
48399      * @cfg {Object} fck toolbar set (Basic or Default)
48400      */
48401     toolbarSet : 'Basic',
48402     /**
48403      * @cfg {Object} fck BasePath
48404      */ 
48405     basePath : '/fckeditor/',
48406     
48407     
48408     frame : false,
48409     
48410     value : '',
48411     
48412    
48413     onRender : function(ct, position)
48414     {
48415         if(!this.el){
48416             this.defaultAutoCreate = {
48417                 tag: "textarea",
48418                 style:"width:300px;height:60px;",
48419                 autocomplete: "new-password"
48420             };
48421         }
48422         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48423         /*
48424         if(this.grow){
48425             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48426             if(this.preventScrollbars){
48427                 this.el.setStyle("overflow", "hidden");
48428             }
48429             this.el.setHeight(this.growMin);
48430         }
48431         */
48432         //console.log('onrender' + this.getId() );
48433         Roo.form.FCKeditor.editors[this.getId()] = this;
48434          
48435
48436         this.replaceTextarea() ;
48437         
48438     },
48439     
48440     getEditor : function() {
48441         return this.fckEditor;
48442     },
48443     /**
48444      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48445      * @param {Mixed} value The value to set
48446      */
48447     
48448     
48449     setValue : function(value)
48450     {
48451         //console.log('setValue: ' + value);
48452         
48453         if(typeof(value) == 'undefined') { // not sure why this is happending...
48454             return;
48455         }
48456         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48457         
48458         //if(!this.el || !this.getEditor()) {
48459         //    this.value = value;
48460             //this.setValue.defer(100,this,[value]);    
48461         //    return;
48462         //} 
48463         
48464         if(!this.getEditor()) {
48465             return;
48466         }
48467         
48468         this.getEditor().SetData(value);
48469         
48470         //
48471
48472     },
48473
48474     /**
48475      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48476      * @return {Mixed} value The field value
48477      */
48478     getValue : function()
48479     {
48480         
48481         if (this.frame && this.frame.dom.style.display == 'none') {
48482             return Roo.form.FCKeditor.superclass.getValue.call(this);
48483         }
48484         
48485         if(!this.el || !this.getEditor()) {
48486            
48487            // this.getValue.defer(100,this); 
48488             return this.value;
48489         }
48490        
48491         
48492         var value=this.getEditor().GetData();
48493         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48494         return Roo.form.FCKeditor.superclass.getValue.call(this);
48495         
48496
48497     },
48498
48499     /**
48500      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48501      * @return {Mixed} value The field value
48502      */
48503     getRawValue : function()
48504     {
48505         if (this.frame && this.frame.dom.style.display == 'none') {
48506             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48507         }
48508         
48509         if(!this.el || !this.getEditor()) {
48510             //this.getRawValue.defer(100,this); 
48511             return this.value;
48512             return;
48513         }
48514         
48515         
48516         
48517         var value=this.getEditor().GetData();
48518         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48519         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48520          
48521     },
48522     
48523     setSize : function(w,h) {
48524         
48525         
48526         
48527         //if (this.frame && this.frame.dom.style.display == 'none') {
48528         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48529         //    return;
48530         //}
48531         //if(!this.el || !this.getEditor()) {
48532         //    this.setSize.defer(100,this, [w,h]); 
48533         //    return;
48534         //}
48535         
48536         
48537         
48538         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48539         
48540         this.frame.dom.setAttribute('width', w);
48541         this.frame.dom.setAttribute('height', h);
48542         this.frame.setSize(w,h);
48543         
48544     },
48545     
48546     toggleSourceEdit : function(value) {
48547         
48548       
48549          
48550         this.el.dom.style.display = value ? '' : 'none';
48551         this.frame.dom.style.display = value ?  'none' : '';
48552         
48553     },
48554     
48555     
48556     focus: function(tag)
48557     {
48558         if (this.frame.dom.style.display == 'none') {
48559             return Roo.form.FCKeditor.superclass.focus.call(this);
48560         }
48561         if(!this.el || !this.getEditor()) {
48562             this.focus.defer(100,this, [tag]); 
48563             return;
48564         }
48565         
48566         
48567         
48568         
48569         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48570         this.getEditor().Focus();
48571         if (tgs.length) {
48572             if (!this.getEditor().Selection.GetSelection()) {
48573                 this.focus.defer(100,this, [tag]); 
48574                 return;
48575             }
48576             
48577             
48578             var r = this.getEditor().EditorDocument.createRange();
48579             r.setStart(tgs[0],0);
48580             r.setEnd(tgs[0],0);
48581             this.getEditor().Selection.GetSelection().removeAllRanges();
48582             this.getEditor().Selection.GetSelection().addRange(r);
48583             this.getEditor().Focus();
48584         }
48585         
48586     },
48587     
48588     
48589     
48590     replaceTextarea : function()
48591     {
48592         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48593             return ;
48594         }
48595         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48596         //{
48597             // We must check the elements firstly using the Id and then the name.
48598         var oTextarea = document.getElementById( this.getId() );
48599         
48600         var colElementsByName = document.getElementsByName( this.getId() ) ;
48601          
48602         oTextarea.style.display = 'none' ;
48603
48604         if ( oTextarea.tabIndex ) {            
48605             this.TabIndex = oTextarea.tabIndex ;
48606         }
48607         
48608         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48609         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48610         this.frame = Roo.get(this.getId() + '___Frame')
48611     },
48612     
48613     _getConfigHtml : function()
48614     {
48615         var sConfig = '' ;
48616
48617         for ( var o in this.fckconfig ) {
48618             sConfig += sConfig.length > 0  ? '&amp;' : '';
48619             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48620         }
48621
48622         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48623     },
48624     
48625     
48626     _getIFrameHtml : function()
48627     {
48628         var sFile = 'fckeditor.html' ;
48629         /* no idea what this is about..
48630         try
48631         {
48632             if ( (/fcksource=true/i).test( window.top.location.search ) )
48633                 sFile = 'fckeditor.original.html' ;
48634         }
48635         catch (e) { 
48636         */
48637
48638         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48639         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48640         
48641         
48642         var html = '<iframe id="' + this.getId() +
48643             '___Frame" src="' + sLink +
48644             '" width="' + this.width +
48645             '" height="' + this.height + '"' +
48646             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48647             ' frameborder="0" scrolling="no"></iframe>' ;
48648
48649         return html ;
48650     },
48651     
48652     _insertHtmlBefore : function( html, element )
48653     {
48654         if ( element.insertAdjacentHTML )       {
48655             // IE
48656             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48657         } else { // Gecko
48658             var oRange = document.createRange() ;
48659             oRange.setStartBefore( element ) ;
48660             var oFragment = oRange.createContextualFragment( html );
48661             element.parentNode.insertBefore( oFragment, element ) ;
48662         }
48663     }
48664     
48665     
48666   
48667     
48668     
48669     
48670     
48671
48672 });
48673
48674 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48675
48676 function FCKeditor_OnComplete(editorInstance){
48677     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48678     f.fckEditor = editorInstance;
48679     //console.log("loaded");
48680     f.fireEvent('editorinit', f, editorInstance);
48681
48682   
48683
48684  
48685
48686
48687
48688
48689
48690
48691
48692
48693
48694
48695
48696
48697
48698
48699
48700 //<script type="text/javascript">
48701 /**
48702  * @class Roo.form.GridField
48703  * @extends Roo.form.Field
48704  * Embed a grid (or editable grid into a form)
48705  * STATUS ALPHA
48706  * 
48707  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48708  * it needs 
48709  * xgrid.store = Roo.data.Store
48710  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48711  * xgrid.store.reader = Roo.data.JsonReader 
48712  * 
48713  * 
48714  * @constructor
48715  * Creates a new GridField
48716  * @param {Object} config Configuration options
48717  */
48718 Roo.form.GridField = function(config){
48719     Roo.form.GridField.superclass.constructor.call(this, config);
48720      
48721 };
48722
48723 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48724     /**
48725      * @cfg {Number} width  - used to restrict width of grid..
48726      */
48727     width : 100,
48728     /**
48729      * @cfg {Number} height - used to restrict height of grid..
48730      */
48731     height : 50,
48732      /**
48733      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48734          * 
48735          *}
48736      */
48737     xgrid : false, 
48738     /**
48739      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48740      * {tag: "input", type: "checkbox", autocomplete: "off"})
48741      */
48742    // defaultAutoCreate : { tag: 'div' },
48743     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48744     /**
48745      * @cfg {String} addTitle Text to include for adding a title.
48746      */
48747     addTitle : false,
48748     //
48749     onResize : function(){
48750         Roo.form.Field.superclass.onResize.apply(this, arguments);
48751     },
48752
48753     initEvents : function(){
48754         // Roo.form.Checkbox.superclass.initEvents.call(this);
48755         // has no events...
48756        
48757     },
48758
48759
48760     getResizeEl : function(){
48761         return this.wrap;
48762     },
48763
48764     getPositionEl : function(){
48765         return this.wrap;
48766     },
48767
48768     // private
48769     onRender : function(ct, position){
48770         
48771         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48772         var style = this.style;
48773         delete this.style;
48774         
48775         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48776         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48777         this.viewEl = this.wrap.createChild({ tag: 'div' });
48778         if (style) {
48779             this.viewEl.applyStyles(style);
48780         }
48781         if (this.width) {
48782             this.viewEl.setWidth(this.width);
48783         }
48784         if (this.height) {
48785             this.viewEl.setHeight(this.height);
48786         }
48787         //if(this.inputValue !== undefined){
48788         //this.setValue(this.value);
48789         
48790         
48791         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48792         
48793         
48794         this.grid.render();
48795         this.grid.getDataSource().on('remove', this.refreshValue, this);
48796         this.grid.getDataSource().on('update', this.refreshValue, this);
48797         this.grid.on('afteredit', this.refreshValue, this);
48798  
48799     },
48800      
48801     
48802     /**
48803      * Sets the value of the item. 
48804      * @param {String} either an object  or a string..
48805      */
48806     setValue : function(v){
48807         //this.value = v;
48808         v = v || []; // empty set..
48809         // this does not seem smart - it really only affects memoryproxy grids..
48810         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48811             var ds = this.grid.getDataSource();
48812             // assumes a json reader..
48813             var data = {}
48814             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48815             ds.loadData( data);
48816         }
48817         // clear selection so it does not get stale.
48818         if (this.grid.sm) { 
48819             this.grid.sm.clearSelections();
48820         }
48821         
48822         Roo.form.GridField.superclass.setValue.call(this, v);
48823         this.refreshValue();
48824         // should load data in the grid really....
48825     },
48826     
48827     // private
48828     refreshValue: function() {
48829          var val = [];
48830         this.grid.getDataSource().each(function(r) {
48831             val.push(r.data);
48832         });
48833         this.el.dom.value = Roo.encode(val);
48834     }
48835     
48836      
48837     
48838     
48839 });/*
48840  * Based on:
48841  * Ext JS Library 1.1.1
48842  * Copyright(c) 2006-2007, Ext JS, LLC.
48843  *
48844  * Originally Released Under LGPL - original licence link has changed is not relivant.
48845  *
48846  * Fork - LGPL
48847  * <script type="text/javascript">
48848  */
48849 /**
48850  * @class Roo.form.DisplayField
48851  * @extends Roo.form.Field
48852  * A generic Field to display non-editable data.
48853  * @cfg {Boolean} closable (true|false) default false
48854  * @constructor
48855  * Creates a new Display Field item.
48856  * @param {Object} config Configuration options
48857  */
48858 Roo.form.DisplayField = function(config){
48859     Roo.form.DisplayField.superclass.constructor.call(this, config);
48860     
48861     this.addEvents({
48862         /**
48863          * @event close
48864          * Fires after the click the close btn
48865              * @param {Roo.form.DisplayField} this
48866              */
48867         close : true
48868     });
48869 };
48870
48871 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48872     inputType:      'hidden',
48873     allowBlank:     true,
48874     readOnly:         true,
48875     
48876  
48877     /**
48878      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48879      */
48880     focusClass : undefined,
48881     /**
48882      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48883      */
48884     fieldClass: 'x-form-field',
48885     
48886      /**
48887      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48888      */
48889     valueRenderer: undefined,
48890     
48891     width: 100,
48892     /**
48893      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48894      * {tag: "input", type: "checkbox", autocomplete: "off"})
48895      */
48896      
48897  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48898  
48899     closable : false,
48900     
48901     onResize : function(){
48902         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48903         
48904     },
48905
48906     initEvents : function(){
48907         // Roo.form.Checkbox.superclass.initEvents.call(this);
48908         // has no events...
48909         
48910         if(this.closable){
48911             this.closeEl.on('click', this.onClose, this);
48912         }
48913        
48914     },
48915
48916
48917     getResizeEl : function(){
48918         return this.wrap;
48919     },
48920
48921     getPositionEl : function(){
48922         return this.wrap;
48923     },
48924
48925     // private
48926     onRender : function(ct, position){
48927         
48928         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48929         //if(this.inputValue !== undefined){
48930         this.wrap = this.el.wrap();
48931         
48932         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48933         
48934         if(this.closable){
48935             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48936         }
48937         
48938         if (this.bodyStyle) {
48939             this.viewEl.applyStyles(this.bodyStyle);
48940         }
48941         //this.viewEl.setStyle('padding', '2px');
48942         
48943         this.setValue(this.value);
48944         
48945     },
48946 /*
48947     // private
48948     initValue : Roo.emptyFn,
48949
48950   */
48951
48952         // private
48953     onClick : function(){
48954         
48955     },
48956
48957     /**
48958      * Sets the checked state of the checkbox.
48959      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48960      */
48961     setValue : function(v){
48962         this.value = v;
48963         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48964         // this might be called before we have a dom element..
48965         if (!this.viewEl) {
48966             return;
48967         }
48968         this.viewEl.dom.innerHTML = html;
48969         Roo.form.DisplayField.superclass.setValue.call(this, v);
48970
48971     },
48972     
48973     onClose : function(e)
48974     {
48975         e.preventDefault();
48976         
48977         this.fireEvent('close', this);
48978     }
48979 });/*
48980  * 
48981  * Licence- LGPL
48982  * 
48983  */
48984
48985 /**
48986  * @class Roo.form.DayPicker
48987  * @extends Roo.form.Field
48988  * A Day picker show [M] [T] [W] ....
48989  * @constructor
48990  * Creates a new Day Picker
48991  * @param {Object} config Configuration options
48992  */
48993 Roo.form.DayPicker= function(config){
48994     Roo.form.DayPicker.superclass.constructor.call(this, config);
48995      
48996 };
48997
48998 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
48999     /**
49000      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49001      */
49002     focusClass : undefined,
49003     /**
49004      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49005      */
49006     fieldClass: "x-form-field",
49007    
49008     /**
49009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49010      * {tag: "input", type: "checkbox", autocomplete: "off"})
49011      */
49012     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49013     
49014    
49015     actionMode : 'viewEl', 
49016     //
49017     // private
49018  
49019     inputType : 'hidden',
49020     
49021      
49022     inputElement: false, // real input element?
49023     basedOn: false, // ????
49024     
49025     isFormField: true, // not sure where this is needed!!!!
49026
49027     onResize : function(){
49028         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49029         if(!this.boxLabel){
49030             this.el.alignTo(this.wrap, 'c-c');
49031         }
49032     },
49033
49034     initEvents : function(){
49035         Roo.form.Checkbox.superclass.initEvents.call(this);
49036         this.el.on("click", this.onClick,  this);
49037         this.el.on("change", this.onClick,  this);
49038     },
49039
49040
49041     getResizeEl : function(){
49042         return this.wrap;
49043     },
49044
49045     getPositionEl : function(){
49046         return this.wrap;
49047     },
49048
49049     
49050     // private
49051     onRender : function(ct, position){
49052         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49053        
49054         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49055         
49056         var r1 = '<table><tr>';
49057         var r2 = '<tr class="x-form-daypick-icons">';
49058         for (var i=0; i < 7; i++) {
49059             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49060             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49061         }
49062         
49063         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49064         viewEl.select('img').on('click', this.onClick, this);
49065         this.viewEl = viewEl;   
49066         
49067         
49068         // this will not work on Chrome!!!
49069         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49070         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49071         
49072         
49073           
49074
49075     },
49076
49077     // private
49078     initValue : Roo.emptyFn,
49079
49080     /**
49081      * Returns the checked state of the checkbox.
49082      * @return {Boolean} True if checked, else false
49083      */
49084     getValue : function(){
49085         return this.el.dom.value;
49086         
49087     },
49088
49089         // private
49090     onClick : function(e){ 
49091         //this.setChecked(!this.checked);
49092         Roo.get(e.target).toggleClass('x-menu-item-checked');
49093         this.refreshValue();
49094         //if(this.el.dom.checked != this.checked){
49095         //    this.setValue(this.el.dom.checked);
49096        // }
49097     },
49098     
49099     // private
49100     refreshValue : function()
49101     {
49102         var val = '';
49103         this.viewEl.select('img',true).each(function(e,i,n)  {
49104             val += e.is(".x-menu-item-checked") ? String(n) : '';
49105         });
49106         this.setValue(val, true);
49107     },
49108
49109     /**
49110      * Sets the checked state of the checkbox.
49111      * On is always based on a string comparison between inputValue and the param.
49112      * @param {Boolean/String} value - the value to set 
49113      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49114      */
49115     setValue : function(v,suppressEvent){
49116         if (!this.el.dom) {
49117             return;
49118         }
49119         var old = this.el.dom.value ;
49120         this.el.dom.value = v;
49121         if (suppressEvent) {
49122             return ;
49123         }
49124          
49125         // update display..
49126         this.viewEl.select('img',true).each(function(e,i,n)  {
49127             
49128             var on = e.is(".x-menu-item-checked");
49129             var newv = v.indexOf(String(n)) > -1;
49130             if (on != newv) {
49131                 e.toggleClass('x-menu-item-checked');
49132             }
49133             
49134         });
49135         
49136         
49137         this.fireEvent('change', this, v, old);
49138         
49139         
49140     },
49141    
49142     // handle setting of hidden value by some other method!!?!?
49143     setFromHidden: function()
49144     {
49145         if(!this.el){
49146             return;
49147         }
49148         //console.log("SET FROM HIDDEN");
49149         //alert('setFrom hidden');
49150         this.setValue(this.el.dom.value);
49151     },
49152     
49153     onDestroy : function()
49154     {
49155         if(this.viewEl){
49156             Roo.get(this.viewEl).remove();
49157         }
49158          
49159         Roo.form.DayPicker.superclass.onDestroy.call(this);
49160     }
49161
49162 });/*
49163  * RooJS Library 1.1.1
49164  * Copyright(c) 2008-2011  Alan Knowles
49165  *
49166  * License - LGPL
49167  */
49168  
49169
49170 /**
49171  * @class Roo.form.ComboCheck
49172  * @extends Roo.form.ComboBox
49173  * A combobox for multiple select items.
49174  *
49175  * FIXME - could do with a reset button..
49176  * 
49177  * @constructor
49178  * Create a new ComboCheck
49179  * @param {Object} config Configuration options
49180  */
49181 Roo.form.ComboCheck = function(config){
49182     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49183     // should verify some data...
49184     // like
49185     // hiddenName = required..
49186     // displayField = required
49187     // valudField == required
49188     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49189     var _t = this;
49190     Roo.each(req, function(e) {
49191         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49192             throw "Roo.form.ComboCheck : missing value for: " + e;
49193         }
49194     });
49195     
49196     
49197 };
49198
49199 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49200      
49201      
49202     editable : false,
49203      
49204     selectedClass: 'x-menu-item-checked', 
49205     
49206     // private
49207     onRender : function(ct, position){
49208         var _t = this;
49209         
49210         
49211         
49212         if(!this.tpl){
49213             var cls = 'x-combo-list';
49214
49215             
49216             this.tpl =  new Roo.Template({
49217                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49218                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49219                    '<span>{' + this.displayField + '}</span>' +
49220                     '</div>' 
49221                 
49222             });
49223         }
49224  
49225         
49226         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49227         this.view.singleSelect = false;
49228         this.view.multiSelect = true;
49229         this.view.toggleSelect = true;
49230         this.pageTb.add(new Roo.Toolbar.Fill(), {
49231             
49232             text: 'Done',
49233             handler: function()
49234             {
49235                 _t.collapse();
49236             }
49237         });
49238     },
49239     
49240     onViewOver : function(e, t){
49241         // do nothing...
49242         return;
49243         
49244     },
49245     
49246     onViewClick : function(doFocus,index){
49247         return;
49248         
49249     },
49250     select: function () {
49251         //Roo.log("SELECT CALLED");
49252     },
49253      
49254     selectByValue : function(xv, scrollIntoView){
49255         var ar = this.getValueArray();
49256         var sels = [];
49257         
49258         Roo.each(ar, function(v) {
49259             if(v === undefined || v === null){
49260                 return;
49261             }
49262             var r = this.findRecord(this.valueField, v);
49263             if(r){
49264                 sels.push(this.store.indexOf(r))
49265                 
49266             }
49267         },this);
49268         this.view.select(sels);
49269         return false;
49270     },
49271     
49272     
49273     
49274     onSelect : function(record, index){
49275        // Roo.log("onselect Called");
49276        // this is only called by the clear button now..
49277         this.view.clearSelections();
49278         this.setValue('[]');
49279         if (this.value != this.valueBefore) {
49280             this.fireEvent('change', this, this.value, this.valueBefore);
49281             this.valueBefore = this.value;
49282         }
49283     },
49284     getValueArray : function()
49285     {
49286         var ar = [] ;
49287         
49288         try {
49289             //Roo.log(this.value);
49290             if (typeof(this.value) == 'undefined') {
49291                 return [];
49292             }
49293             var ar = Roo.decode(this.value);
49294             return  ar instanceof Array ? ar : []; //?? valid?
49295             
49296         } catch(e) {
49297             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49298             return [];
49299         }
49300          
49301     },
49302     expand : function ()
49303     {
49304         
49305         Roo.form.ComboCheck.superclass.expand.call(this);
49306         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49307         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49308         
49309
49310     },
49311     
49312     collapse : function(){
49313         Roo.form.ComboCheck.superclass.collapse.call(this);
49314         var sl = this.view.getSelectedIndexes();
49315         var st = this.store;
49316         var nv = [];
49317         var tv = [];
49318         var r;
49319         Roo.each(sl, function(i) {
49320             r = st.getAt(i);
49321             nv.push(r.get(this.valueField));
49322         },this);
49323         this.setValue(Roo.encode(nv));
49324         if (this.value != this.valueBefore) {
49325
49326             this.fireEvent('change', this, this.value, this.valueBefore);
49327             this.valueBefore = this.value;
49328         }
49329         
49330     },
49331     
49332     setValue : function(v){
49333         // Roo.log(v);
49334         this.value = v;
49335         
49336         var vals = this.getValueArray();
49337         var tv = [];
49338         Roo.each(vals, function(k) {
49339             var r = this.findRecord(this.valueField, k);
49340             if(r){
49341                 tv.push(r.data[this.displayField]);
49342             }else if(this.valueNotFoundText !== undefined){
49343                 tv.push( this.valueNotFoundText );
49344             }
49345         },this);
49346        // Roo.log(tv);
49347         
49348         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49349         this.hiddenField.value = v;
49350         this.value = v;
49351     }
49352     
49353 });/*
49354  * Based on:
49355  * Ext JS Library 1.1.1
49356  * Copyright(c) 2006-2007, Ext JS, LLC.
49357  *
49358  * Originally Released Under LGPL - original licence link has changed is not relivant.
49359  *
49360  * Fork - LGPL
49361  * <script type="text/javascript">
49362  */
49363  
49364 /**
49365  * @class Roo.form.Signature
49366  * @extends Roo.form.Field
49367  * Signature field.  
49368  * @constructor
49369  * 
49370  * @param {Object} config Configuration options
49371  */
49372
49373 Roo.form.Signature = function(config){
49374     Roo.form.Signature.superclass.constructor.call(this, config);
49375     
49376     this.addEvents({// not in used??
49377          /**
49378          * @event confirm
49379          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49380              * @param {Roo.form.Signature} combo This combo box
49381              */
49382         'confirm' : true,
49383         /**
49384          * @event reset
49385          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49386              * @param {Roo.form.ComboBox} combo This combo box
49387              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49388              */
49389         'reset' : true
49390     });
49391 };
49392
49393 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49394     /**
49395      * @cfg {Object} labels Label to use when rendering a form.
49396      * defaults to 
49397      * labels : { 
49398      *      clear : "Clear",
49399      *      confirm : "Confirm"
49400      *  }
49401      */
49402     labels : { 
49403         clear : "Clear",
49404         confirm : "Confirm"
49405     },
49406     /**
49407      * @cfg {Number} width The signature panel width (defaults to 300)
49408      */
49409     width: 300,
49410     /**
49411      * @cfg {Number} height The signature panel height (defaults to 100)
49412      */
49413     height : 100,
49414     /**
49415      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49416      */
49417     allowBlank : false,
49418     
49419     //private
49420     // {Object} signPanel The signature SVG panel element (defaults to {})
49421     signPanel : {},
49422     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49423     isMouseDown : false,
49424     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49425     isConfirmed : false,
49426     // {String} signatureTmp SVG mapping string (defaults to empty string)
49427     signatureTmp : '',
49428     
49429     
49430     defaultAutoCreate : { // modified by initCompnoent..
49431         tag: "input",
49432         type:"hidden"
49433     },
49434
49435     // private
49436     onRender : function(ct, position){
49437         
49438         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49439         
49440         this.wrap = this.el.wrap({
49441             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49442         });
49443         
49444         this.createToolbar(this);
49445         this.signPanel = this.wrap.createChild({
49446                 tag: 'div',
49447                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49448             }, this.el
49449         );
49450             
49451         this.svgID = Roo.id();
49452         this.svgEl = this.signPanel.createChild({
49453               xmlns : 'http://www.w3.org/2000/svg',
49454               tag : 'svg',
49455               id : this.svgID + "-svg",
49456               width: this.width,
49457               height: this.height,
49458               viewBox: '0 0 '+this.width+' '+this.height,
49459               cn : [
49460                 {
49461                     tag: "rect",
49462                     id: this.svgID + "-svg-r",
49463                     width: this.width,
49464                     height: this.height,
49465                     fill: "#ffa"
49466                 },
49467                 {
49468                     tag: "line",
49469                     id: this.svgID + "-svg-l",
49470                     x1: "0", // start
49471                     y1: (this.height*0.8), // start set the line in 80% of height
49472                     x2: this.width, // end
49473                     y2: (this.height*0.8), // end set the line in 80% of height
49474                     'stroke': "#666",
49475                     'stroke-width': "1",
49476                     'stroke-dasharray': "3",
49477                     'shape-rendering': "crispEdges",
49478                     'pointer-events': "none"
49479                 },
49480                 {
49481                     tag: "path",
49482                     id: this.svgID + "-svg-p",
49483                     'stroke': "navy",
49484                     'stroke-width': "3",
49485                     'fill': "none",
49486                     'pointer-events': 'none'
49487                 }
49488               ]
49489         });
49490         this.createSVG();
49491         this.svgBox = this.svgEl.dom.getScreenCTM();
49492     },
49493     createSVG : function(){ 
49494         var svg = this.signPanel;
49495         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49496         var t = this;
49497
49498         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49499         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49500         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49501         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49502         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49503         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49504         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49505         
49506     },
49507     isTouchEvent : function(e){
49508         return e.type.match(/^touch/);
49509     },
49510     getCoords : function (e) {
49511         var pt    = this.svgEl.dom.createSVGPoint();
49512         pt.x = e.clientX; 
49513         pt.y = e.clientY;
49514         if (this.isTouchEvent(e)) {
49515             pt.x =  e.targetTouches[0].clientX;
49516             pt.y = e.targetTouches[0].clientY;
49517         }
49518         var a = this.svgEl.dom.getScreenCTM();
49519         var b = a.inverse();
49520         var mx = pt.matrixTransform(b);
49521         return mx.x + ',' + mx.y;
49522     },
49523     //mouse event headler 
49524     down : function (e) {
49525         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49526         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49527         
49528         this.isMouseDown = true;
49529         
49530         e.preventDefault();
49531     },
49532     move : function (e) {
49533         if (this.isMouseDown) {
49534             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49535             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49536         }
49537         
49538         e.preventDefault();
49539     },
49540     up : function (e) {
49541         this.isMouseDown = false;
49542         var sp = this.signatureTmp.split(' ');
49543         
49544         if(sp.length > 1){
49545             if(!sp[sp.length-2].match(/^L/)){
49546                 sp.pop();
49547                 sp.pop();
49548                 sp.push("");
49549                 this.signatureTmp = sp.join(" ");
49550             }
49551         }
49552         if(this.getValue() != this.signatureTmp){
49553             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49554             this.isConfirmed = false;
49555         }
49556         e.preventDefault();
49557     },
49558     
49559     /**
49560      * Protected method that will not generally be called directly. It
49561      * is called when the editor creates its toolbar. Override this method if you need to
49562      * add custom toolbar buttons.
49563      * @param {HtmlEditor} editor
49564      */
49565     createToolbar : function(editor){
49566          function btn(id, toggle, handler){
49567             var xid = fid + '-'+ id ;
49568             return {
49569                 id : xid,
49570                 cmd : id,
49571                 cls : 'x-btn-icon x-edit-'+id,
49572                 enableToggle:toggle !== false,
49573                 scope: editor, // was editor...
49574                 handler:handler||editor.relayBtnCmd,
49575                 clickEvent:'mousedown',
49576                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49577                 tabIndex:-1
49578             };
49579         }
49580         
49581         
49582         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49583         this.tb = tb;
49584         this.tb.add(
49585            {
49586                 cls : ' x-signature-btn x-signature-'+id,
49587                 scope: editor, // was editor...
49588                 handler: this.reset,
49589                 clickEvent:'mousedown',
49590                 text: this.labels.clear
49591             },
49592             {
49593                  xtype : 'Fill',
49594                  xns: Roo.Toolbar
49595             }, 
49596             {
49597                 cls : '  x-signature-btn x-signature-'+id,
49598                 scope: editor, // was editor...
49599                 handler: this.confirmHandler,
49600                 clickEvent:'mousedown',
49601                 text: this.labels.confirm
49602             }
49603         );
49604     
49605     },
49606     //public
49607     /**
49608      * when user is clicked confirm then show this image.....
49609      * 
49610      * @return {String} Image Data URI
49611      */
49612     getImageDataURI : function(){
49613         var svg = this.svgEl.dom.parentNode.innerHTML;
49614         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49615         return src; 
49616     },
49617     /**
49618      * 
49619      * @return {Boolean} this.isConfirmed
49620      */
49621     getConfirmed : function(){
49622         return this.isConfirmed;
49623     },
49624     /**
49625      * 
49626      * @return {Number} this.width
49627      */
49628     getWidth : function(){
49629         return this.width;
49630     },
49631     /**
49632      * 
49633      * @return {Number} this.height
49634      */
49635     getHeight : function(){
49636         return this.height;
49637     },
49638     // private
49639     getSignature : function(){
49640         return this.signatureTmp;
49641     },
49642     // private
49643     reset : function(){
49644         this.signatureTmp = '';
49645         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49646         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49647         this.isConfirmed = false;
49648         Roo.form.Signature.superclass.reset.call(this);
49649     },
49650     setSignature : function(s){
49651         this.signatureTmp = s;
49652         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49653         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49654         this.setValue(s);
49655         this.isConfirmed = false;
49656         Roo.form.Signature.superclass.reset.call(this);
49657     }, 
49658     test : function(){
49659 //        Roo.log(this.signPanel.dom.contentWindow.up())
49660     },
49661     //private
49662     setConfirmed : function(){
49663         
49664         
49665         
49666 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49667     },
49668     // private
49669     confirmHandler : function(){
49670         if(!this.getSignature()){
49671             return;
49672         }
49673         
49674         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49675         this.setValue(this.getSignature());
49676         this.isConfirmed = true;
49677         
49678         this.fireEvent('confirm', this);
49679     },
49680     // private
49681     // Subclasses should provide the validation implementation by overriding this
49682     validateValue : function(value){
49683         if(this.allowBlank){
49684             return true;
49685         }
49686         
49687         if(this.isConfirmed){
49688             return true;
49689         }
49690         return false;
49691     }
49692 });/*
49693  * Based on:
49694  * Ext JS Library 1.1.1
49695  * Copyright(c) 2006-2007, Ext JS, LLC.
49696  *
49697  * Originally Released Under LGPL - original licence link has changed is not relivant.
49698  *
49699  * Fork - LGPL
49700  * <script type="text/javascript">
49701  */
49702  
49703
49704 /**
49705  * @class Roo.form.ComboBox
49706  * @extends Roo.form.TriggerField
49707  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49708  * @constructor
49709  * Create a new ComboBox.
49710  * @param {Object} config Configuration options
49711  */
49712 Roo.form.Select = function(config){
49713     Roo.form.Select.superclass.constructor.call(this, config);
49714      
49715 };
49716
49717 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49718     /**
49719      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49720      */
49721     /**
49722      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49723      * rendering into an Roo.Editor, defaults to false)
49724      */
49725     /**
49726      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49727      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49728      */
49729     /**
49730      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49731      */
49732     /**
49733      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49734      * the dropdown list (defaults to undefined, with no header element)
49735      */
49736
49737      /**
49738      * @cfg {String/Roo.Template} tpl The template to use to render the output
49739      */
49740      
49741     // private
49742     defaultAutoCreate : {tag: "select"  },
49743     /**
49744      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49745      */
49746     listWidth: undefined,
49747     /**
49748      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49749      * mode = 'remote' or 'text' if mode = 'local')
49750      */
49751     displayField: undefined,
49752     /**
49753      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49754      * mode = 'remote' or 'value' if mode = 'local'). 
49755      * Note: use of a valueField requires the user make a selection
49756      * in order for a value to be mapped.
49757      */
49758     valueField: undefined,
49759     
49760     
49761     /**
49762      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49763      * field's data value (defaults to the underlying DOM element's name)
49764      */
49765     hiddenName: undefined,
49766     /**
49767      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49768      */
49769     listClass: '',
49770     /**
49771      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49772      */
49773     selectedClass: 'x-combo-selected',
49774     /**
49775      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49776      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49777      * which displays a downward arrow icon).
49778      */
49779     triggerClass : 'x-form-arrow-trigger',
49780     /**
49781      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49782      */
49783     shadow:'sides',
49784     /**
49785      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49786      * anchor positions (defaults to 'tl-bl')
49787      */
49788     listAlign: 'tl-bl?',
49789     /**
49790      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49791      */
49792     maxHeight: 300,
49793     /**
49794      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49795      * query specified by the allQuery config option (defaults to 'query')
49796      */
49797     triggerAction: 'query',
49798     /**
49799      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49800      * (defaults to 4, does not apply if editable = false)
49801      */
49802     minChars : 4,
49803     /**
49804      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49805      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49806      */
49807     typeAhead: false,
49808     /**
49809      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49810      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49811      */
49812     queryDelay: 500,
49813     /**
49814      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49815      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49816      */
49817     pageSize: 0,
49818     /**
49819      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49820      * when editable = true (defaults to false)
49821      */
49822     selectOnFocus:false,
49823     /**
49824      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49825      */
49826     queryParam: 'query',
49827     /**
49828      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49829      * when mode = 'remote' (defaults to 'Loading...')
49830      */
49831     loadingText: 'Loading...',
49832     /**
49833      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49834      */
49835     resizable: false,
49836     /**
49837      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49838      */
49839     handleHeight : 8,
49840     /**
49841      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49842      * traditional select (defaults to true)
49843      */
49844     editable: true,
49845     /**
49846      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49847      */
49848     allQuery: '',
49849     /**
49850      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49851      */
49852     mode: 'remote',
49853     /**
49854      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49855      * listWidth has a higher value)
49856      */
49857     minListWidth : 70,
49858     /**
49859      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49860      * allow the user to set arbitrary text into the field (defaults to false)
49861      */
49862     forceSelection:false,
49863     /**
49864      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49865      * if typeAhead = true (defaults to 250)
49866      */
49867     typeAheadDelay : 250,
49868     /**
49869      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49870      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49871      */
49872     valueNotFoundText : undefined,
49873     
49874     /**
49875      * @cfg {String} defaultValue The value displayed after loading the store.
49876      */
49877     defaultValue: '',
49878     
49879     /**
49880      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49881      */
49882     blockFocus : false,
49883     
49884     /**
49885      * @cfg {Boolean} disableClear Disable showing of clear button.
49886      */
49887     disableClear : false,
49888     /**
49889      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49890      */
49891     alwaysQuery : false,
49892     
49893     //private
49894     addicon : false,
49895     editicon: false,
49896     
49897     // element that contains real text value.. (when hidden is used..)
49898      
49899     // private
49900     onRender : function(ct, position){
49901         Roo.form.Field.prototype.onRender.call(this, ct, position);
49902         
49903         if(this.store){
49904             this.store.on('beforeload', this.onBeforeLoad, this);
49905             this.store.on('load', this.onLoad, this);
49906             this.store.on('loadexception', this.onLoadException, this);
49907             this.store.load({});
49908         }
49909         
49910         
49911         
49912     },
49913
49914     // private
49915     initEvents : function(){
49916         //Roo.form.ComboBox.superclass.initEvents.call(this);
49917  
49918     },
49919
49920     onDestroy : function(){
49921        
49922         if(this.store){
49923             this.store.un('beforeload', this.onBeforeLoad, this);
49924             this.store.un('load', this.onLoad, this);
49925             this.store.un('loadexception', this.onLoadException, this);
49926         }
49927         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49928     },
49929
49930     // private
49931     fireKey : function(e){
49932         if(e.isNavKeyPress() && !this.list.isVisible()){
49933             this.fireEvent("specialkey", this, e);
49934         }
49935     },
49936
49937     // private
49938     onResize: function(w, h){
49939         
49940         return; 
49941     
49942         
49943     },
49944
49945     /**
49946      * Allow or prevent the user from directly editing the field text.  If false is passed,
49947      * the user will only be able to select from the items defined in the dropdown list.  This method
49948      * is the runtime equivalent of setting the 'editable' config option at config time.
49949      * @param {Boolean} value True to allow the user to directly edit the field text
49950      */
49951     setEditable : function(value){
49952          
49953     },
49954
49955     // private
49956     onBeforeLoad : function(){
49957         
49958         Roo.log("Select before load");
49959         return;
49960     
49961         this.innerList.update(this.loadingText ?
49962                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49963         //this.restrictHeight();
49964         this.selectedIndex = -1;
49965     },
49966
49967     // private
49968     onLoad : function(){
49969
49970     
49971         var dom = this.el.dom;
49972         dom.innerHTML = '';
49973          var od = dom.ownerDocument;
49974          
49975         if (this.emptyText) {
49976             var op = od.createElement('option');
49977             op.setAttribute('value', '');
49978             op.innerHTML = String.format('{0}', this.emptyText);
49979             dom.appendChild(op);
49980         }
49981         if(this.store.getCount() > 0){
49982            
49983             var vf = this.valueField;
49984             var df = this.displayField;
49985             this.store.data.each(function(r) {
49986                 // which colmsn to use... testing - cdoe / title..
49987                 var op = od.createElement('option');
49988                 op.setAttribute('value', r.data[vf]);
49989                 op.innerHTML = String.format('{0}', r.data[df]);
49990                 dom.appendChild(op);
49991             });
49992             if (typeof(this.defaultValue != 'undefined')) {
49993                 this.setValue(this.defaultValue);
49994             }
49995             
49996              
49997         }else{
49998             //this.onEmptyResults();
49999         }
50000         //this.el.focus();
50001     },
50002     // private
50003     onLoadException : function()
50004     {
50005         dom.innerHTML = '';
50006             
50007         Roo.log("Select on load exception");
50008         return;
50009     
50010         this.collapse();
50011         Roo.log(this.store.reader.jsonData);
50012         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50013             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50014         }
50015         
50016         
50017     },
50018     // private
50019     onTypeAhead : function(){
50020          
50021     },
50022
50023     // private
50024     onSelect : function(record, index){
50025         Roo.log('on select?');
50026         return;
50027         if(this.fireEvent('beforeselect', this, record, index) !== false){
50028             this.setFromData(index > -1 ? record.data : false);
50029             this.collapse();
50030             this.fireEvent('select', this, record, index);
50031         }
50032     },
50033
50034     /**
50035      * Returns the currently selected field value or empty string if no value is set.
50036      * @return {String} value The selected value
50037      */
50038     getValue : function(){
50039         var dom = this.el.dom;
50040         this.value = dom.options[dom.selectedIndex].value;
50041         return this.value;
50042         
50043     },
50044
50045     /**
50046      * Clears any text/value currently set in the field
50047      */
50048     clearValue : function(){
50049         this.value = '';
50050         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50051         
50052     },
50053
50054     /**
50055      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50056      * will be displayed in the field.  If the value does not match the data value of an existing item,
50057      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50058      * Otherwise the field will be blank (although the value will still be set).
50059      * @param {String} value The value to match
50060      */
50061     setValue : function(v){
50062         var d = this.el.dom;
50063         for (var i =0; i < d.options.length;i++) {
50064             if (v == d.options[i].value) {
50065                 d.selectedIndex = i;
50066                 this.value = v;
50067                 return;
50068             }
50069         }
50070         this.clearValue();
50071     },
50072     /**
50073      * @property {Object} the last set data for the element
50074      */
50075     
50076     lastData : false,
50077     /**
50078      * Sets the value of the field based on a object which is related to the record format for the store.
50079      * @param {Object} value the value to set as. or false on reset?
50080      */
50081     setFromData : function(o){
50082         Roo.log('setfrom data?');
50083          
50084         
50085         
50086     },
50087     // private
50088     reset : function(){
50089         this.clearValue();
50090     },
50091     // private
50092     findRecord : function(prop, value){
50093         
50094         return false;
50095     
50096         var record;
50097         if(this.store.getCount() > 0){
50098             this.store.each(function(r){
50099                 if(r.data[prop] == value){
50100                     record = r;
50101                     return false;
50102                 }
50103                 return true;
50104             });
50105         }
50106         return record;
50107     },
50108     
50109     getName: function()
50110     {
50111         // returns hidden if it's set..
50112         if (!this.rendered) {return ''};
50113         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50114         
50115     },
50116      
50117
50118     
50119
50120     // private
50121     onEmptyResults : function(){
50122         Roo.log('empty results');
50123         //this.collapse();
50124     },
50125
50126     /**
50127      * Returns true if the dropdown list is expanded, else false.
50128      */
50129     isExpanded : function(){
50130         return false;
50131     },
50132
50133     /**
50134      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50135      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50136      * @param {String} value The data value of the item to select
50137      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50138      * selected item if it is not currently in view (defaults to true)
50139      * @return {Boolean} True if the value matched an item in the list, else false
50140      */
50141     selectByValue : function(v, scrollIntoView){
50142         Roo.log('select By Value');
50143         return false;
50144     
50145         if(v !== undefined && v !== null){
50146             var r = this.findRecord(this.valueField || this.displayField, v);
50147             if(r){
50148                 this.select(this.store.indexOf(r), scrollIntoView);
50149                 return true;
50150             }
50151         }
50152         return false;
50153     },
50154
50155     /**
50156      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50157      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50158      * @param {Number} index The zero-based index of the list item to select
50159      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50160      * selected item if it is not currently in view (defaults to true)
50161      */
50162     select : function(index, scrollIntoView){
50163         Roo.log('select ');
50164         return  ;
50165         
50166         this.selectedIndex = index;
50167         this.view.select(index);
50168         if(scrollIntoView !== false){
50169             var el = this.view.getNode(index);
50170             if(el){
50171                 this.innerList.scrollChildIntoView(el, false);
50172             }
50173         }
50174     },
50175
50176       
50177
50178     // private
50179     validateBlur : function(){
50180         
50181         return;
50182         
50183     },
50184
50185     // private
50186     initQuery : function(){
50187         this.doQuery(this.getRawValue());
50188     },
50189
50190     // private
50191     doForce : function(){
50192         if(this.el.dom.value.length > 0){
50193             this.el.dom.value =
50194                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50195              
50196         }
50197     },
50198
50199     /**
50200      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50201      * query allowing the query action to be canceled if needed.
50202      * @param {String} query The SQL query to execute
50203      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50204      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50205      * saved in the current store (defaults to false)
50206      */
50207     doQuery : function(q, forceAll){
50208         
50209         Roo.log('doQuery?');
50210         if(q === undefined || q === null){
50211             q = '';
50212         }
50213         var qe = {
50214             query: q,
50215             forceAll: forceAll,
50216             combo: this,
50217             cancel:false
50218         };
50219         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50220             return false;
50221         }
50222         q = qe.query;
50223         forceAll = qe.forceAll;
50224         if(forceAll === true || (q.length >= this.minChars)){
50225             if(this.lastQuery != q || this.alwaysQuery){
50226                 this.lastQuery = q;
50227                 if(this.mode == 'local'){
50228                     this.selectedIndex = -1;
50229                     if(forceAll){
50230                         this.store.clearFilter();
50231                     }else{
50232                         this.store.filter(this.displayField, q);
50233                     }
50234                     this.onLoad();
50235                 }else{
50236                     this.store.baseParams[this.queryParam] = q;
50237                     this.store.load({
50238                         params: this.getParams(q)
50239                     });
50240                     this.expand();
50241                 }
50242             }else{
50243                 this.selectedIndex = -1;
50244                 this.onLoad();   
50245             }
50246         }
50247     },
50248
50249     // private
50250     getParams : function(q){
50251         var p = {};
50252         //p[this.queryParam] = q;
50253         if(this.pageSize){
50254             p.start = 0;
50255             p.limit = this.pageSize;
50256         }
50257         return p;
50258     },
50259
50260     /**
50261      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50262      */
50263     collapse : function(){
50264         
50265     },
50266
50267     // private
50268     collapseIf : function(e){
50269         
50270     },
50271
50272     /**
50273      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50274      */
50275     expand : function(){
50276         
50277     } ,
50278
50279     // private
50280      
50281
50282     /** 
50283     * @cfg {Boolean} grow 
50284     * @hide 
50285     */
50286     /** 
50287     * @cfg {Number} growMin 
50288     * @hide 
50289     */
50290     /** 
50291     * @cfg {Number} growMax 
50292     * @hide 
50293     */
50294     /**
50295      * @hide
50296      * @method autoSize
50297      */
50298     
50299     setWidth : function()
50300     {
50301         
50302     },
50303     getResizeEl : function(){
50304         return this.el;
50305     }
50306 });//<script type="text/javasscript">
50307  
50308
50309 /**
50310  * @class Roo.DDView
50311  * A DnD enabled version of Roo.View.
50312  * @param {Element/String} container The Element in which to create the View.
50313  * @param {String} tpl The template string used to create the markup for each element of the View
50314  * @param {Object} config The configuration properties. These include all the config options of
50315  * {@link Roo.View} plus some specific to this class.<br>
50316  * <p>
50317  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50318  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50319  * <p>
50320  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50321 .x-view-drag-insert-above {
50322         border-top:1px dotted #3366cc;
50323 }
50324 .x-view-drag-insert-below {
50325         border-bottom:1px dotted #3366cc;
50326 }
50327 </code></pre>
50328  * 
50329  */
50330  
50331 Roo.DDView = function(container, tpl, config) {
50332     Roo.DDView.superclass.constructor.apply(this, arguments);
50333     this.getEl().setStyle("outline", "0px none");
50334     this.getEl().unselectable();
50335     if (this.dragGroup) {
50336                 this.setDraggable(this.dragGroup.split(","));
50337     }
50338     if (this.dropGroup) {
50339                 this.setDroppable(this.dropGroup.split(","));
50340     }
50341     if (this.deletable) {
50342         this.setDeletable();
50343     }
50344     this.isDirtyFlag = false;
50345         this.addEvents({
50346                 "drop" : true
50347         });
50348 };
50349
50350 Roo.extend(Roo.DDView, Roo.View, {
50351 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50352 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50353 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50354 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50355
50356         isFormField: true,
50357
50358         reset: Roo.emptyFn,
50359         
50360         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50361
50362         validate: function() {
50363                 return true;
50364         },
50365         
50366         destroy: function() {
50367                 this.purgeListeners();
50368                 this.getEl.removeAllListeners();
50369                 this.getEl().remove();
50370                 if (this.dragZone) {
50371                         if (this.dragZone.destroy) {
50372                                 this.dragZone.destroy();
50373                         }
50374                 }
50375                 if (this.dropZone) {
50376                         if (this.dropZone.destroy) {
50377                                 this.dropZone.destroy();
50378                         }
50379                 }
50380         },
50381
50382 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50383         getName: function() {
50384                 return this.name;
50385         },
50386
50387 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50388         setValue: function(v) {
50389                 if (!this.store) {
50390                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50391                 }
50392                 var data = {};
50393                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50394                 this.store.proxy = new Roo.data.MemoryProxy(data);
50395                 this.store.load();
50396         },
50397
50398 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50399         getValue: function() {
50400                 var result = '(';
50401                 this.store.each(function(rec) {
50402                         result += rec.id + ',';
50403                 });
50404                 return result.substr(0, result.length - 1) + ')';
50405         },
50406         
50407         getIds: function() {
50408                 var i = 0, result = new Array(this.store.getCount());
50409                 this.store.each(function(rec) {
50410                         result[i++] = rec.id;
50411                 });
50412                 return result;
50413         },
50414         
50415         isDirty: function() {
50416                 return this.isDirtyFlag;
50417         },
50418
50419 /**
50420  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50421  *      whole Element becomes the target, and this causes the drop gesture to append.
50422  */
50423     getTargetFromEvent : function(e) {
50424                 var target = e.getTarget();
50425                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50426                 target = target.parentNode;
50427                 }
50428                 if (!target) {
50429                         target = this.el.dom.lastChild || this.el.dom;
50430                 }
50431                 return target;
50432     },
50433
50434 /**
50435  *      Create the drag data which consists of an object which has the property "ddel" as
50436  *      the drag proxy element. 
50437  */
50438     getDragData : function(e) {
50439         var target = this.findItemFromChild(e.getTarget());
50440                 if(target) {
50441                         this.handleSelection(e);
50442                         var selNodes = this.getSelectedNodes();
50443             var dragData = {
50444                 source: this,
50445                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50446                 nodes: selNodes,
50447                 records: []
50448                         };
50449                         var selectedIndices = this.getSelectedIndexes();
50450                         for (var i = 0; i < selectedIndices.length; i++) {
50451                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50452                         }
50453                         if (selNodes.length == 1) {
50454                                 dragData.ddel = target.cloneNode(true); // the div element
50455                         } else {
50456                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50457                                 div.className = 'multi-proxy';
50458                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50459                                         div.appendChild(selNodes[i].cloneNode(true));
50460                                 }
50461                                 dragData.ddel = div;
50462                         }
50463             //console.log(dragData)
50464             //console.log(dragData.ddel.innerHTML)
50465                         return dragData;
50466                 }
50467         //console.log('nodragData')
50468                 return false;
50469     },
50470     
50471 /**     Specify to which ddGroup items in this DDView may be dragged. */
50472     setDraggable: function(ddGroup) {
50473         if (ddGroup instanceof Array) {
50474                 Roo.each(ddGroup, this.setDraggable, this);
50475                 return;
50476         }
50477         if (this.dragZone) {
50478                 this.dragZone.addToGroup(ddGroup);
50479         } else {
50480                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50481                                 containerScroll: true,
50482                                 ddGroup: ddGroup 
50483
50484                         });
50485 //                      Draggability implies selection. DragZone's mousedown selects the element.
50486                         if (!this.multiSelect) { this.singleSelect = true; }
50487
50488 //                      Wire the DragZone's handlers up to methods in *this*
50489                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50490                 }
50491     },
50492
50493 /**     Specify from which ddGroup this DDView accepts drops. */
50494     setDroppable: function(ddGroup) {
50495         if (ddGroup instanceof Array) {
50496                 Roo.each(ddGroup, this.setDroppable, this);
50497                 return;
50498         }
50499         if (this.dropZone) {
50500                 this.dropZone.addToGroup(ddGroup);
50501         } else {
50502                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50503                                 containerScroll: true,
50504                                 ddGroup: ddGroup
50505                         });
50506
50507 //                      Wire the DropZone's handlers up to methods in *this*
50508                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50509                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50510                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50511                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50512                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50513                 }
50514     },
50515
50516 /**     Decide whether to drop above or below a View node. */
50517     getDropPoint : function(e, n, dd){
50518         if (n == this.el.dom) { return "above"; }
50519                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50520                 var c = t + (b - t) / 2;
50521                 var y = Roo.lib.Event.getPageY(e);
50522                 if(y <= c) {
50523                         return "above";
50524                 }else{
50525                         return "below";
50526                 }
50527     },
50528
50529     onNodeEnter : function(n, dd, e, data){
50530                 return false;
50531     },
50532     
50533     onNodeOver : function(n, dd, e, data){
50534                 var pt = this.getDropPoint(e, n, dd);
50535                 // set the insert point style on the target node
50536                 var dragElClass = this.dropNotAllowed;
50537                 if (pt) {
50538                         var targetElClass;
50539                         if (pt == "above"){
50540                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50541                                 targetElClass = "x-view-drag-insert-above";
50542                         } else {
50543                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50544                                 targetElClass = "x-view-drag-insert-below";
50545                         }
50546                         if (this.lastInsertClass != targetElClass){
50547                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50548                                 this.lastInsertClass = targetElClass;
50549                         }
50550                 }
50551                 return dragElClass;
50552         },
50553
50554     onNodeOut : function(n, dd, e, data){
50555                 this.removeDropIndicators(n);
50556     },
50557
50558     onNodeDrop : function(n, dd, e, data){
50559         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50560                 return false;
50561         }
50562         var pt = this.getDropPoint(e, n, dd);
50563                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50564                 if (pt == "below") { insertAt++; }
50565                 for (var i = 0; i < data.records.length; i++) {
50566                         var r = data.records[i];
50567                         var dup = this.store.getById(r.id);
50568                         if (dup && (dd != this.dragZone)) {
50569                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50570                         } else {
50571                                 if (data.copy) {
50572                                         this.store.insert(insertAt++, r.copy());
50573                                 } else {
50574                                         data.source.isDirtyFlag = true;
50575                                         r.store.remove(r);
50576                                         this.store.insert(insertAt++, r);
50577                                 }
50578                                 this.isDirtyFlag = true;
50579                         }
50580                 }
50581                 this.dragZone.cachedTarget = null;
50582                 return true;
50583     },
50584
50585     removeDropIndicators : function(n){
50586                 if(n){
50587                         Roo.fly(n).removeClass([
50588                                 "x-view-drag-insert-above",
50589                                 "x-view-drag-insert-below"]);
50590                         this.lastInsertClass = "_noclass";
50591                 }
50592     },
50593
50594 /**
50595  *      Utility method. Add a delete option to the DDView's context menu.
50596  *      @param {String} imageUrl The URL of the "delete" icon image.
50597  */
50598         setDeletable: function(imageUrl) {
50599                 if (!this.singleSelect && !this.multiSelect) {
50600                         this.singleSelect = true;
50601                 }
50602                 var c = this.getContextMenu();
50603                 this.contextMenu.on("itemclick", function(item) {
50604                         switch (item.id) {
50605                                 case "delete":
50606                                         this.remove(this.getSelectedIndexes());
50607                                         break;
50608                         }
50609                 }, this);
50610                 this.contextMenu.add({
50611                         icon: imageUrl,
50612                         id: "delete",
50613                         text: 'Delete'
50614                 });
50615         },
50616         
50617 /**     Return the context menu for this DDView. */
50618         getContextMenu: function() {
50619                 if (!this.contextMenu) {
50620 //                      Create the View's context menu
50621                         this.contextMenu = new Roo.menu.Menu({
50622                                 id: this.id + "-contextmenu"
50623                         });
50624                         this.el.on("contextmenu", this.showContextMenu, this);
50625                 }
50626                 return this.contextMenu;
50627         },
50628         
50629         disableContextMenu: function() {
50630                 if (this.contextMenu) {
50631                         this.el.un("contextmenu", this.showContextMenu, this);
50632                 }
50633         },
50634
50635         showContextMenu: function(e, item) {
50636         item = this.findItemFromChild(e.getTarget());
50637                 if (item) {
50638                         e.stopEvent();
50639                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50640                         this.contextMenu.showAt(e.getXY());
50641             }
50642     },
50643
50644 /**
50645  *      Remove {@link Roo.data.Record}s at the specified indices.
50646  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50647  */
50648     remove: function(selectedIndices) {
50649                 selectedIndices = [].concat(selectedIndices);
50650                 for (var i = 0; i < selectedIndices.length; i++) {
50651                         var rec = this.store.getAt(selectedIndices[i]);
50652                         this.store.remove(rec);
50653                 }
50654     },
50655
50656 /**
50657  *      Double click fires the event, but also, if this is draggable, and there is only one other
50658  *      related DropZone, it transfers the selected node.
50659  */
50660     onDblClick : function(e){
50661         var item = this.findItemFromChild(e.getTarget());
50662         if(item){
50663             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50664                 return false;
50665             }
50666             if (this.dragGroup) {
50667                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50668                     while (targets.indexOf(this.dropZone) > -1) {
50669                             targets.remove(this.dropZone);
50670                                 }
50671                     if (targets.length == 1) {
50672                                         this.dragZone.cachedTarget = null;
50673                         var el = Roo.get(targets[0].getEl());
50674                         var box = el.getBox(true);
50675                         targets[0].onNodeDrop(el.dom, {
50676                                 target: el.dom,
50677                                 xy: [box.x, box.y + box.height - 1]
50678                         }, null, this.getDragData(e));
50679                     }
50680                 }
50681         }
50682     },
50683     
50684     handleSelection: function(e) {
50685                 this.dragZone.cachedTarget = null;
50686         var item = this.findItemFromChild(e.getTarget());
50687         if (!item) {
50688                 this.clearSelections(true);
50689                 return;
50690         }
50691                 if (item && (this.multiSelect || this.singleSelect)){
50692                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50693                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50694                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50695                                 this.unselect(item);
50696                         } else {
50697                                 this.select(item, this.multiSelect && e.ctrlKey);
50698                                 this.lastSelection = item;
50699                         }
50700                 }
50701     },
50702
50703     onItemClick : function(item, index, e){
50704                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50705                         return false;
50706                 }
50707                 return true;
50708     },
50709
50710     unselect : function(nodeInfo, suppressEvent){
50711                 var node = this.getNode(nodeInfo);
50712                 if(node && this.isSelected(node)){
50713                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50714                                 Roo.fly(node).removeClass(this.selectedClass);
50715                                 this.selections.remove(node);
50716                                 if(!suppressEvent){
50717                                         this.fireEvent("selectionchange", this, this.selections);
50718                                 }
50719                         }
50720                 }
50721     }
50722 });
50723 /*
50724  * Based on:
50725  * Ext JS Library 1.1.1
50726  * Copyright(c) 2006-2007, Ext JS, LLC.
50727  *
50728  * Originally Released Under LGPL - original licence link has changed is not relivant.
50729  *
50730  * Fork - LGPL
50731  * <script type="text/javascript">
50732  */
50733  
50734 /**
50735  * @class Roo.LayoutManager
50736  * @extends Roo.util.Observable
50737  * Base class for layout managers.
50738  */
50739 Roo.LayoutManager = function(container, config){
50740     Roo.LayoutManager.superclass.constructor.call(this);
50741     this.el = Roo.get(container);
50742     // ie scrollbar fix
50743     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50744         document.body.scroll = "no";
50745     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50746         this.el.position('relative');
50747     }
50748     this.id = this.el.id;
50749     this.el.addClass("x-layout-container");
50750     /** false to disable window resize monitoring @type Boolean */
50751     this.monitorWindowResize = true;
50752     this.regions = {};
50753     this.addEvents({
50754         /**
50755          * @event layout
50756          * Fires when a layout is performed. 
50757          * @param {Roo.LayoutManager} this
50758          */
50759         "layout" : true,
50760         /**
50761          * @event regionresized
50762          * Fires when the user resizes a region. 
50763          * @param {Roo.LayoutRegion} region The resized region
50764          * @param {Number} newSize The new size (width for east/west, height for north/south)
50765          */
50766         "regionresized" : true,
50767         /**
50768          * @event regioncollapsed
50769          * Fires when a region is collapsed. 
50770          * @param {Roo.LayoutRegion} region The collapsed region
50771          */
50772         "regioncollapsed" : true,
50773         /**
50774          * @event regionexpanded
50775          * Fires when a region is expanded.  
50776          * @param {Roo.LayoutRegion} region The expanded region
50777          */
50778         "regionexpanded" : true
50779     });
50780     this.updating = false;
50781     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50782 };
50783
50784 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50785     /**
50786      * Returns true if this layout is currently being updated
50787      * @return {Boolean}
50788      */
50789     isUpdating : function(){
50790         return this.updating; 
50791     },
50792     
50793     /**
50794      * Suspend the LayoutManager from doing auto-layouts while
50795      * making multiple add or remove calls
50796      */
50797     beginUpdate : function(){
50798         this.updating = true;    
50799     },
50800     
50801     /**
50802      * Restore auto-layouts and optionally disable the manager from performing a layout
50803      * @param {Boolean} noLayout true to disable a layout update 
50804      */
50805     endUpdate : function(noLayout){
50806         this.updating = false;
50807         if(!noLayout){
50808             this.layout();
50809         }    
50810     },
50811     
50812     layout: function(){
50813         
50814     },
50815     
50816     onRegionResized : function(region, newSize){
50817         this.fireEvent("regionresized", region, newSize);
50818         this.layout();
50819     },
50820     
50821     onRegionCollapsed : function(region){
50822         this.fireEvent("regioncollapsed", region);
50823     },
50824     
50825     onRegionExpanded : function(region){
50826         this.fireEvent("regionexpanded", region);
50827     },
50828         
50829     /**
50830      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50831      * performs box-model adjustments.
50832      * @return {Object} The size as an object {width: (the width), height: (the height)}
50833      */
50834     getViewSize : function(){
50835         var size;
50836         if(this.el.dom != document.body){
50837             size = this.el.getSize();
50838         }else{
50839             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50840         }
50841         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50842         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50843         return size;
50844     },
50845     
50846     /**
50847      * Returns the Element this layout is bound to.
50848      * @return {Roo.Element}
50849      */
50850     getEl : function(){
50851         return this.el;
50852     },
50853     
50854     /**
50855      * Returns the specified region.
50856      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50857      * @return {Roo.LayoutRegion}
50858      */
50859     getRegion : function(target){
50860         return this.regions[target.toLowerCase()];
50861     },
50862     
50863     onWindowResize : function(){
50864         if(this.monitorWindowResize){
50865             this.layout();
50866         }
50867     }
50868 });/*
50869  * Based on:
50870  * Ext JS Library 1.1.1
50871  * Copyright(c) 2006-2007, Ext JS, LLC.
50872  *
50873  * Originally Released Under LGPL - original licence link has changed is not relivant.
50874  *
50875  * Fork - LGPL
50876  * <script type="text/javascript">
50877  */
50878 /**
50879  * @class Roo.BorderLayout
50880  * @extends Roo.LayoutManager
50881  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50882  * please see: <br><br>
50883  * <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>
50884  * <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>
50885  * Example:
50886  <pre><code>
50887  var layout = new Roo.BorderLayout(document.body, {
50888     north: {
50889         initialSize: 25,
50890         titlebar: false
50891     },
50892     west: {
50893         split:true,
50894         initialSize: 200,
50895         minSize: 175,
50896         maxSize: 400,
50897         titlebar: true,
50898         collapsible: true
50899     },
50900     east: {
50901         split:true,
50902         initialSize: 202,
50903         minSize: 175,
50904         maxSize: 400,
50905         titlebar: true,
50906         collapsible: true
50907     },
50908     south: {
50909         split:true,
50910         initialSize: 100,
50911         minSize: 100,
50912         maxSize: 200,
50913         titlebar: true,
50914         collapsible: true
50915     },
50916     center: {
50917         titlebar: true,
50918         autoScroll:true,
50919         resizeTabs: true,
50920         minTabWidth: 50,
50921         preferredTabWidth: 150
50922     }
50923 });
50924
50925 // shorthand
50926 var CP = Roo.ContentPanel;
50927
50928 layout.beginUpdate();
50929 layout.add("north", new CP("north", "North"));
50930 layout.add("south", new CP("south", {title: "South", closable: true}));
50931 layout.add("west", new CP("west", {title: "West"}));
50932 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50933 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50934 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50935 layout.getRegion("center").showPanel("center1");
50936 layout.endUpdate();
50937 </code></pre>
50938
50939 <b>The container the layout is rendered into can be either the body element or any other element.
50940 If it is not the body element, the container needs to either be an absolute positioned element,
50941 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50942 the container size if it is not the body element.</b>
50943
50944 * @constructor
50945 * Create a new BorderLayout
50946 * @param {String/HTMLElement/Element} container The container this layout is bound to
50947 * @param {Object} config Configuration options
50948  */
50949 Roo.BorderLayout = function(container, config){
50950     config = config || {};
50951     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50952     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50953     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50954         var target = this.factory.validRegions[i];
50955         if(config[target]){
50956             this.addRegion(target, config[target]);
50957         }
50958     }
50959 };
50960
50961 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50962     /**
50963      * Creates and adds a new region if it doesn't already exist.
50964      * @param {String} target The target region key (north, south, east, west or center).
50965      * @param {Object} config The regions config object
50966      * @return {BorderLayoutRegion} The new region
50967      */
50968     addRegion : function(target, config){
50969         if(!this.regions[target]){
50970             var r = this.factory.create(target, this, config);
50971             this.bindRegion(target, r);
50972         }
50973         return this.regions[target];
50974     },
50975
50976     // private (kinda)
50977     bindRegion : function(name, r){
50978         this.regions[name] = r;
50979         r.on("visibilitychange", this.layout, this);
50980         r.on("paneladded", this.layout, this);
50981         r.on("panelremoved", this.layout, this);
50982         r.on("invalidated", this.layout, this);
50983         r.on("resized", this.onRegionResized, this);
50984         r.on("collapsed", this.onRegionCollapsed, this);
50985         r.on("expanded", this.onRegionExpanded, this);
50986     },
50987
50988     /**
50989      * Performs a layout update.
50990      */
50991     layout : function(){
50992         if(this.updating) {
50993             return;
50994         }
50995         var size = this.getViewSize();
50996         var w = size.width;
50997         var h = size.height;
50998         var centerW = w;
50999         var centerH = h;
51000         var centerY = 0;
51001         var centerX = 0;
51002         //var x = 0, y = 0;
51003
51004         var rs = this.regions;
51005         var north = rs["north"];
51006         var south = rs["south"]; 
51007         var west = rs["west"];
51008         var east = rs["east"];
51009         var center = rs["center"];
51010         //if(this.hideOnLayout){ // not supported anymore
51011             //c.el.setStyle("display", "none");
51012         //}
51013         if(north && north.isVisible()){
51014             var b = north.getBox();
51015             var m = north.getMargins();
51016             b.width = w - (m.left+m.right);
51017             b.x = m.left;
51018             b.y = m.top;
51019             centerY = b.height + b.y + m.bottom;
51020             centerH -= centerY;
51021             north.updateBox(this.safeBox(b));
51022         }
51023         if(south && south.isVisible()){
51024             var b = south.getBox();
51025             var m = south.getMargins();
51026             b.width = w - (m.left+m.right);
51027             b.x = m.left;
51028             var totalHeight = (b.height + m.top + m.bottom);
51029             b.y = h - totalHeight + m.top;
51030             centerH -= totalHeight;
51031             south.updateBox(this.safeBox(b));
51032         }
51033         if(west && west.isVisible()){
51034             var b = west.getBox();
51035             var m = west.getMargins();
51036             b.height = centerH - (m.top+m.bottom);
51037             b.x = m.left;
51038             b.y = centerY + m.top;
51039             var totalWidth = (b.width + m.left + m.right);
51040             centerX += totalWidth;
51041             centerW -= totalWidth;
51042             west.updateBox(this.safeBox(b));
51043         }
51044         if(east && east.isVisible()){
51045             var b = east.getBox();
51046             var m = east.getMargins();
51047             b.height = centerH - (m.top+m.bottom);
51048             var totalWidth = (b.width + m.left + m.right);
51049             b.x = w - totalWidth + m.left;
51050             b.y = centerY + m.top;
51051             centerW -= totalWidth;
51052             east.updateBox(this.safeBox(b));
51053         }
51054         if(center){
51055             var m = center.getMargins();
51056             var centerBox = {
51057                 x: centerX + m.left,
51058                 y: centerY + m.top,
51059                 width: centerW - (m.left+m.right),
51060                 height: centerH - (m.top+m.bottom)
51061             };
51062             //if(this.hideOnLayout){
51063                 //center.el.setStyle("display", "block");
51064             //}
51065             center.updateBox(this.safeBox(centerBox));
51066         }
51067         this.el.repaint();
51068         this.fireEvent("layout", this);
51069     },
51070
51071     // private
51072     safeBox : function(box){
51073         box.width = Math.max(0, box.width);
51074         box.height = Math.max(0, box.height);
51075         return box;
51076     },
51077
51078     /**
51079      * Adds a ContentPanel (or subclass) to this layout.
51080      * @param {String} target The target region key (north, south, east, west or center).
51081      * @param {Roo.ContentPanel} panel The panel to add
51082      * @return {Roo.ContentPanel} The added panel
51083      */
51084     add : function(target, panel){
51085          
51086         target = target.toLowerCase();
51087         return this.regions[target].add(panel);
51088     },
51089
51090     /**
51091      * Remove a ContentPanel (or subclass) to this layout.
51092      * @param {String} target The target region key (north, south, east, west or center).
51093      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51094      * @return {Roo.ContentPanel} The removed panel
51095      */
51096     remove : function(target, panel){
51097         target = target.toLowerCase();
51098         return this.regions[target].remove(panel);
51099     },
51100
51101     /**
51102      * Searches all regions for a panel with the specified id
51103      * @param {String} panelId
51104      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51105      */
51106     findPanel : function(panelId){
51107         var rs = this.regions;
51108         for(var target in rs){
51109             if(typeof rs[target] != "function"){
51110                 var p = rs[target].getPanel(panelId);
51111                 if(p){
51112                     return p;
51113                 }
51114             }
51115         }
51116         return null;
51117     },
51118
51119     /**
51120      * Searches all regions for a panel with the specified id and activates (shows) it.
51121      * @param {String/ContentPanel} panelId The panels id or the panel itself
51122      * @return {Roo.ContentPanel} The shown panel or null
51123      */
51124     showPanel : function(panelId) {
51125       var rs = this.regions;
51126       for(var target in rs){
51127          var r = rs[target];
51128          if(typeof r != "function"){
51129             if(r.hasPanel(panelId)){
51130                return r.showPanel(panelId);
51131             }
51132          }
51133       }
51134       return null;
51135    },
51136
51137    /**
51138      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51139      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51140      */
51141     restoreState : function(provider){
51142         if(!provider){
51143             provider = Roo.state.Manager;
51144         }
51145         var sm = new Roo.LayoutStateManager();
51146         sm.init(this, provider);
51147     },
51148
51149     /**
51150      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51151      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51152      * a valid ContentPanel config object.  Example:
51153      * <pre><code>
51154 // Create the main layout
51155 var layout = new Roo.BorderLayout('main-ct', {
51156     west: {
51157         split:true,
51158         minSize: 175,
51159         titlebar: true
51160     },
51161     center: {
51162         title:'Components'
51163     }
51164 }, 'main-ct');
51165
51166 // Create and add multiple ContentPanels at once via configs
51167 layout.batchAdd({
51168    west: {
51169        id: 'source-files',
51170        autoCreate:true,
51171        title:'Ext Source Files',
51172        autoScroll:true,
51173        fitToFrame:true
51174    },
51175    center : {
51176        el: cview,
51177        autoScroll:true,
51178        fitToFrame:true,
51179        toolbar: tb,
51180        resizeEl:'cbody'
51181    }
51182 });
51183 </code></pre>
51184      * @param {Object} regions An object containing ContentPanel configs by region name
51185      */
51186     batchAdd : function(regions){
51187         this.beginUpdate();
51188         for(var rname in regions){
51189             var lr = this.regions[rname];
51190             if(lr){
51191                 this.addTypedPanels(lr, regions[rname]);
51192             }
51193         }
51194         this.endUpdate();
51195     },
51196
51197     // private
51198     addTypedPanels : function(lr, ps){
51199         if(typeof ps == 'string'){
51200             lr.add(new Roo.ContentPanel(ps));
51201         }
51202         else if(ps instanceof Array){
51203             for(var i =0, len = ps.length; i < len; i++){
51204                 this.addTypedPanels(lr, ps[i]);
51205             }
51206         }
51207         else if(!ps.events){ // raw config?
51208             var el = ps.el;
51209             delete ps.el; // prevent conflict
51210             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51211         }
51212         else {  // panel object assumed!
51213             lr.add(ps);
51214         }
51215     },
51216     /**
51217      * Adds a xtype elements to the layout.
51218      * <pre><code>
51219
51220 layout.addxtype({
51221        xtype : 'ContentPanel',
51222        region: 'west',
51223        items: [ .... ]
51224    }
51225 );
51226
51227 layout.addxtype({
51228         xtype : 'NestedLayoutPanel',
51229         region: 'west',
51230         layout: {
51231            center: { },
51232            west: { }   
51233         },
51234         items : [ ... list of content panels or nested layout panels.. ]
51235    }
51236 );
51237 </code></pre>
51238      * @param {Object} cfg Xtype definition of item to add.
51239      */
51240     addxtype : function(cfg)
51241     {
51242         // basically accepts a pannel...
51243         // can accept a layout region..!?!?
51244         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51245         
51246         if (!cfg.xtype.match(/Panel$/)) {
51247             return false;
51248         }
51249         var ret = false;
51250         
51251         if (typeof(cfg.region) == 'undefined') {
51252             Roo.log("Failed to add Panel, region was not set");
51253             Roo.log(cfg);
51254             return false;
51255         }
51256         var region = cfg.region;
51257         delete cfg.region;
51258         
51259           
51260         var xitems = [];
51261         if (cfg.items) {
51262             xitems = cfg.items;
51263             delete cfg.items;
51264         }
51265         var nb = false;
51266         
51267         switch(cfg.xtype) 
51268         {
51269             case 'ContentPanel':  // ContentPanel (el, cfg)
51270             case 'ScrollPanel':  // ContentPanel (el, cfg)
51271             case 'ViewPanel': 
51272                 if(cfg.autoCreate) {
51273                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51274                 } else {
51275                     var el = this.el.createChild();
51276                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51277                 }
51278                 
51279                 this.add(region, ret);
51280                 break;
51281             
51282             
51283             case 'TreePanel': // our new panel!
51284                 cfg.el = this.el.createChild();
51285                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51286                 this.add(region, ret);
51287                 break;
51288             
51289             case 'NestedLayoutPanel': 
51290                 // create a new Layout (which is  a Border Layout...
51291                 var el = this.el.createChild();
51292                 var clayout = cfg.layout;
51293                 delete cfg.layout;
51294                 clayout.items   = clayout.items  || [];
51295                 // replace this exitems with the clayout ones..
51296                 xitems = clayout.items;
51297                  
51298                 
51299                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51300                     cfg.background = false;
51301                 }
51302                 var layout = new Roo.BorderLayout(el, clayout);
51303                 
51304                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51305                 //console.log('adding nested layout panel '  + cfg.toSource());
51306                 this.add(region, ret);
51307                 nb = {}; /// find first...
51308                 break;
51309                 
51310             case 'GridPanel': 
51311             
51312                 // needs grid and region
51313                 
51314                 //var el = this.getRegion(region).el.createChild();
51315                 var el = this.el.createChild();
51316                 // create the grid first...
51317                 
51318                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51319                 delete cfg.grid;
51320                 if (region == 'center' && this.active ) {
51321                     cfg.background = false;
51322                 }
51323                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51324                 
51325                 this.add(region, ret);
51326                 if (cfg.background) {
51327                     ret.on('activate', function(gp) {
51328                         if (!gp.grid.rendered) {
51329                             gp.grid.render();
51330                         }
51331                     });
51332                 } else {
51333                     grid.render();
51334                 }
51335                 break;
51336            
51337            
51338            
51339                 
51340                 
51341                 
51342             default:
51343                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51344                     
51345                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51346                     this.add(region, ret);
51347                 } else {
51348                 
51349                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51350                     return null;
51351                 }
51352                 
51353              // GridPanel (grid, cfg)
51354             
51355         }
51356         this.beginUpdate();
51357         // add children..
51358         var region = '';
51359         var abn = {};
51360         Roo.each(xitems, function(i)  {
51361             region = nb && i.region ? i.region : false;
51362             
51363             var add = ret.addxtype(i);
51364            
51365             if (region) {
51366                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51367                 if (!i.background) {
51368                     abn[region] = nb[region] ;
51369                 }
51370             }
51371             
51372         });
51373         this.endUpdate();
51374
51375         // make the last non-background panel active..
51376         //if (nb) { Roo.log(abn); }
51377         if (nb) {
51378             
51379             for(var r in abn) {
51380                 region = this.getRegion(r);
51381                 if (region) {
51382                     // tried using nb[r], but it does not work..
51383                      
51384                     region.showPanel(abn[r]);
51385                    
51386                 }
51387             }
51388         }
51389         return ret;
51390         
51391     }
51392 });
51393
51394 /**
51395  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51396  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51397  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51398  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51399  * <pre><code>
51400 // shorthand
51401 var CP = Roo.ContentPanel;
51402
51403 var layout = Roo.BorderLayout.create({
51404     north: {
51405         initialSize: 25,
51406         titlebar: false,
51407         panels: [new CP("north", "North")]
51408     },
51409     west: {
51410         split:true,
51411         initialSize: 200,
51412         minSize: 175,
51413         maxSize: 400,
51414         titlebar: true,
51415         collapsible: true,
51416         panels: [new CP("west", {title: "West"})]
51417     },
51418     east: {
51419         split:true,
51420         initialSize: 202,
51421         minSize: 175,
51422         maxSize: 400,
51423         titlebar: true,
51424         collapsible: true,
51425         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51426     },
51427     south: {
51428         split:true,
51429         initialSize: 100,
51430         minSize: 100,
51431         maxSize: 200,
51432         titlebar: true,
51433         collapsible: true,
51434         panels: [new CP("south", {title: "South", closable: true})]
51435     },
51436     center: {
51437         titlebar: true,
51438         autoScroll:true,
51439         resizeTabs: true,
51440         minTabWidth: 50,
51441         preferredTabWidth: 150,
51442         panels: [
51443             new CP("center1", {title: "Close Me", closable: true}),
51444             new CP("center2", {title: "Center Panel", closable: false})
51445         ]
51446     }
51447 }, document.body);
51448
51449 layout.getRegion("center").showPanel("center1");
51450 </code></pre>
51451  * @param config
51452  * @param targetEl
51453  */
51454 Roo.BorderLayout.create = function(config, targetEl){
51455     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51456     layout.beginUpdate();
51457     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51458     for(var j = 0, jlen = regions.length; j < jlen; j++){
51459         var lr = regions[j];
51460         if(layout.regions[lr] && config[lr].panels){
51461             var r = layout.regions[lr];
51462             var ps = config[lr].panels;
51463             layout.addTypedPanels(r, ps);
51464         }
51465     }
51466     layout.endUpdate();
51467     return layout;
51468 };
51469
51470 // private
51471 Roo.BorderLayout.RegionFactory = {
51472     // private
51473     validRegions : ["north","south","east","west","center"],
51474
51475     // private
51476     create : function(target, mgr, config){
51477         target = target.toLowerCase();
51478         if(config.lightweight || config.basic){
51479             return new Roo.BasicLayoutRegion(mgr, config, target);
51480         }
51481         switch(target){
51482             case "north":
51483                 return new Roo.NorthLayoutRegion(mgr, config);
51484             case "south":
51485                 return new Roo.SouthLayoutRegion(mgr, config);
51486             case "east":
51487                 return new Roo.EastLayoutRegion(mgr, config);
51488             case "west":
51489                 return new Roo.WestLayoutRegion(mgr, config);
51490             case "center":
51491                 return new Roo.CenterLayoutRegion(mgr, config);
51492         }
51493         throw 'Layout region "'+target+'" not supported.';
51494     }
51495 };/*
51496  * Based on:
51497  * Ext JS Library 1.1.1
51498  * Copyright(c) 2006-2007, Ext JS, LLC.
51499  *
51500  * Originally Released Under LGPL - original licence link has changed is not relivant.
51501  *
51502  * Fork - LGPL
51503  * <script type="text/javascript">
51504  */
51505  
51506 /**
51507  * @class Roo.BasicLayoutRegion
51508  * @extends Roo.util.Observable
51509  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51510  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51511  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51512  */
51513 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51514     this.mgr = mgr;
51515     this.position  = pos;
51516     this.events = {
51517         /**
51518          * @scope Roo.BasicLayoutRegion
51519          */
51520         
51521         /**
51522          * @event beforeremove
51523          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51524          * @param {Roo.LayoutRegion} this
51525          * @param {Roo.ContentPanel} panel The panel
51526          * @param {Object} e The cancel event object
51527          */
51528         "beforeremove" : true,
51529         /**
51530          * @event invalidated
51531          * Fires when the layout for this region is changed.
51532          * @param {Roo.LayoutRegion} this
51533          */
51534         "invalidated" : true,
51535         /**
51536          * @event visibilitychange
51537          * Fires when this region is shown or hidden 
51538          * @param {Roo.LayoutRegion} this
51539          * @param {Boolean} visibility true or false
51540          */
51541         "visibilitychange" : true,
51542         /**
51543          * @event paneladded
51544          * Fires when a panel is added. 
51545          * @param {Roo.LayoutRegion} this
51546          * @param {Roo.ContentPanel} panel The panel
51547          */
51548         "paneladded" : true,
51549         /**
51550          * @event panelremoved
51551          * Fires when a panel is removed. 
51552          * @param {Roo.LayoutRegion} this
51553          * @param {Roo.ContentPanel} panel The panel
51554          */
51555         "panelremoved" : true,
51556         /**
51557          * @event beforecollapse
51558          * Fires when this region before collapse.
51559          * @param {Roo.LayoutRegion} this
51560          */
51561         "beforecollapse" : true,
51562         /**
51563          * @event collapsed
51564          * Fires when this region is collapsed.
51565          * @param {Roo.LayoutRegion} this
51566          */
51567         "collapsed" : true,
51568         /**
51569          * @event expanded
51570          * Fires when this region is expanded.
51571          * @param {Roo.LayoutRegion} this
51572          */
51573         "expanded" : true,
51574         /**
51575          * @event slideshow
51576          * Fires when this region is slid into view.
51577          * @param {Roo.LayoutRegion} this
51578          */
51579         "slideshow" : true,
51580         /**
51581          * @event slidehide
51582          * Fires when this region slides out of view. 
51583          * @param {Roo.LayoutRegion} this
51584          */
51585         "slidehide" : true,
51586         /**
51587          * @event panelactivated
51588          * Fires when a panel is activated. 
51589          * @param {Roo.LayoutRegion} this
51590          * @param {Roo.ContentPanel} panel The activated panel
51591          */
51592         "panelactivated" : true,
51593         /**
51594          * @event resized
51595          * Fires when the user resizes this region. 
51596          * @param {Roo.LayoutRegion} this
51597          * @param {Number} newSize The new size (width for east/west, height for north/south)
51598          */
51599         "resized" : true
51600     };
51601     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51602     this.panels = new Roo.util.MixedCollection();
51603     this.panels.getKey = this.getPanelId.createDelegate(this);
51604     this.box = null;
51605     this.activePanel = null;
51606     // ensure listeners are added...
51607     
51608     if (config.listeners || config.events) {
51609         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51610             listeners : config.listeners || {},
51611             events : config.events || {}
51612         });
51613     }
51614     
51615     if(skipConfig !== true){
51616         this.applyConfig(config);
51617     }
51618 };
51619
51620 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51621     getPanelId : function(p){
51622         return p.getId();
51623     },
51624     
51625     applyConfig : function(config){
51626         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51627         this.config = config;
51628         
51629     },
51630     
51631     /**
51632      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51633      * the width, for horizontal (north, south) the height.
51634      * @param {Number} newSize The new width or height
51635      */
51636     resizeTo : function(newSize){
51637         var el = this.el ? this.el :
51638                  (this.activePanel ? this.activePanel.getEl() : null);
51639         if(el){
51640             switch(this.position){
51641                 case "east":
51642                 case "west":
51643                     el.setWidth(newSize);
51644                     this.fireEvent("resized", this, newSize);
51645                 break;
51646                 case "north":
51647                 case "south":
51648                     el.setHeight(newSize);
51649                     this.fireEvent("resized", this, newSize);
51650                 break;                
51651             }
51652         }
51653     },
51654     
51655     getBox : function(){
51656         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51657     },
51658     
51659     getMargins : function(){
51660         return this.margins;
51661     },
51662     
51663     updateBox : function(box){
51664         this.box = box;
51665         var el = this.activePanel.getEl();
51666         el.dom.style.left = box.x + "px";
51667         el.dom.style.top = box.y + "px";
51668         this.activePanel.setSize(box.width, box.height);
51669     },
51670     
51671     /**
51672      * Returns the container element for this region.
51673      * @return {Roo.Element}
51674      */
51675     getEl : function(){
51676         return this.activePanel;
51677     },
51678     
51679     /**
51680      * Returns true if this region is currently visible.
51681      * @return {Boolean}
51682      */
51683     isVisible : function(){
51684         return this.activePanel ? true : false;
51685     },
51686     
51687     setActivePanel : function(panel){
51688         panel = this.getPanel(panel);
51689         if(this.activePanel && this.activePanel != panel){
51690             this.activePanel.setActiveState(false);
51691             this.activePanel.getEl().setLeftTop(-10000,-10000);
51692         }
51693         this.activePanel = panel;
51694         panel.setActiveState(true);
51695         if(this.box){
51696             panel.setSize(this.box.width, this.box.height);
51697         }
51698         this.fireEvent("panelactivated", this, panel);
51699         this.fireEvent("invalidated");
51700     },
51701     
51702     /**
51703      * Show the specified panel.
51704      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51705      * @return {Roo.ContentPanel} The shown panel or null
51706      */
51707     showPanel : function(panel){
51708         if(panel = this.getPanel(panel)){
51709             this.setActivePanel(panel);
51710         }
51711         return panel;
51712     },
51713     
51714     /**
51715      * Get the active panel for this region.
51716      * @return {Roo.ContentPanel} The active panel or null
51717      */
51718     getActivePanel : function(){
51719         return this.activePanel;
51720     },
51721     
51722     /**
51723      * Add the passed ContentPanel(s)
51724      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51725      * @return {Roo.ContentPanel} The panel added (if only one was added)
51726      */
51727     add : function(panel){
51728         if(arguments.length > 1){
51729             for(var i = 0, len = arguments.length; i < len; i++) {
51730                 this.add(arguments[i]);
51731             }
51732             return null;
51733         }
51734         if(this.hasPanel(panel)){
51735             this.showPanel(panel);
51736             return panel;
51737         }
51738         var el = panel.getEl();
51739         if(el.dom.parentNode != this.mgr.el.dom){
51740             this.mgr.el.dom.appendChild(el.dom);
51741         }
51742         if(panel.setRegion){
51743             panel.setRegion(this);
51744         }
51745         this.panels.add(panel);
51746         el.setStyle("position", "absolute");
51747         if(!panel.background){
51748             this.setActivePanel(panel);
51749             if(this.config.initialSize && this.panels.getCount()==1){
51750                 this.resizeTo(this.config.initialSize);
51751             }
51752         }
51753         this.fireEvent("paneladded", this, panel);
51754         return panel;
51755     },
51756     
51757     /**
51758      * Returns true if the panel is in this region.
51759      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51760      * @return {Boolean}
51761      */
51762     hasPanel : function(panel){
51763         if(typeof panel == "object"){ // must be panel obj
51764             panel = panel.getId();
51765         }
51766         return this.getPanel(panel) ? true : false;
51767     },
51768     
51769     /**
51770      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51771      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51772      * @param {Boolean} preservePanel Overrides the config preservePanel option
51773      * @return {Roo.ContentPanel} The panel that was removed
51774      */
51775     remove : function(panel, preservePanel){
51776         panel = this.getPanel(panel);
51777         if(!panel){
51778             return null;
51779         }
51780         var e = {};
51781         this.fireEvent("beforeremove", this, panel, e);
51782         if(e.cancel === true){
51783             return null;
51784         }
51785         var panelId = panel.getId();
51786         this.panels.removeKey(panelId);
51787         return panel;
51788     },
51789     
51790     /**
51791      * Returns the panel specified or null if it's not in this region.
51792      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51793      * @return {Roo.ContentPanel}
51794      */
51795     getPanel : function(id){
51796         if(typeof id == "object"){ // must be panel obj
51797             return id;
51798         }
51799         return this.panels.get(id);
51800     },
51801     
51802     /**
51803      * Returns this regions position (north/south/east/west/center).
51804      * @return {String} 
51805      */
51806     getPosition: function(){
51807         return this.position;    
51808     }
51809 });/*
51810  * Based on:
51811  * Ext JS Library 1.1.1
51812  * Copyright(c) 2006-2007, Ext JS, LLC.
51813  *
51814  * Originally Released Under LGPL - original licence link has changed is not relivant.
51815  *
51816  * Fork - LGPL
51817  * <script type="text/javascript">
51818  */
51819  
51820 /**
51821  * @class Roo.LayoutRegion
51822  * @extends Roo.BasicLayoutRegion
51823  * This class represents a region in a layout manager.
51824  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51825  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51826  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51827  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51828  * @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})
51829  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51830  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51831  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51832  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51833  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51834  * @cfg {String}    title           The title for the region (overrides panel titles)
51835  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51836  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51837  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51838  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51839  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51840  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51841  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51842  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51843  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51844  * @cfg {Boolean}   showPin         True to show a pin button
51845  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51846  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51847  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51848  * @cfg {Number}    width           For East/West panels
51849  * @cfg {Number}    height          For North/South panels
51850  * @cfg {Boolean}   split           To show the splitter
51851  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51852  */
51853 Roo.LayoutRegion = function(mgr, config, pos){
51854     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51855     var dh = Roo.DomHelper;
51856     /** This region's container element 
51857     * @type Roo.Element */
51858     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51859     /** This region's title element 
51860     * @type Roo.Element */
51861
51862     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51863         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51864         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51865     ]}, true);
51866     this.titleEl.enableDisplayMode();
51867     /** This region's title text element 
51868     * @type HTMLElement */
51869     this.titleTextEl = this.titleEl.dom.firstChild;
51870     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51871     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51872     this.closeBtn.enableDisplayMode();
51873     this.closeBtn.on("click", this.closeClicked, this);
51874     this.closeBtn.hide();
51875
51876     this.createBody(config);
51877     this.visible = true;
51878     this.collapsed = false;
51879
51880     if(config.hideWhenEmpty){
51881         this.hide();
51882         this.on("paneladded", this.validateVisibility, this);
51883         this.on("panelremoved", this.validateVisibility, this);
51884     }
51885     this.applyConfig(config);
51886 };
51887
51888 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51889
51890     createBody : function(){
51891         /** This region's body element 
51892         * @type Roo.Element */
51893         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51894     },
51895
51896     applyConfig : function(c){
51897         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51898             var dh = Roo.DomHelper;
51899             if(c.titlebar !== false){
51900                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51901                 this.collapseBtn.on("click", this.collapse, this);
51902                 this.collapseBtn.enableDisplayMode();
51903
51904                 if(c.showPin === true || this.showPin){
51905                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51906                     this.stickBtn.enableDisplayMode();
51907                     this.stickBtn.on("click", this.expand, this);
51908                     this.stickBtn.hide();
51909                 }
51910             }
51911             /** This region's collapsed element
51912             * @type Roo.Element */
51913             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51914                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51915             ]}, true);
51916             if(c.floatable !== false){
51917                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51918                this.collapsedEl.on("click", this.collapseClick, this);
51919             }
51920
51921             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51922                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51923                    id: "message", unselectable: "on", style:{"float":"left"}});
51924                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51925              }
51926             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51927             this.expandBtn.on("click", this.expand, this);
51928         }
51929         if(this.collapseBtn){
51930             this.collapseBtn.setVisible(c.collapsible == true);
51931         }
51932         this.cmargins = c.cmargins || this.cmargins ||
51933                          (this.position == "west" || this.position == "east" ?
51934                              {top: 0, left: 2, right:2, bottom: 0} :
51935                              {top: 2, left: 0, right:0, bottom: 2});
51936         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51937         this.bottomTabs = c.tabPosition != "top";
51938         this.autoScroll = c.autoScroll || false;
51939         if(this.autoScroll){
51940             this.bodyEl.setStyle("overflow", "auto");
51941         }else{
51942             this.bodyEl.setStyle("overflow", "hidden");
51943         }
51944         //if(c.titlebar !== false){
51945             if((!c.titlebar && !c.title) || c.titlebar === false){
51946                 this.titleEl.hide();
51947             }else{
51948                 this.titleEl.show();
51949                 if(c.title){
51950                     this.titleTextEl.innerHTML = c.title;
51951                 }
51952             }
51953         //}
51954         this.duration = c.duration || .30;
51955         this.slideDuration = c.slideDuration || .45;
51956         this.config = c;
51957         if(c.collapsed){
51958             this.collapse(true);
51959         }
51960         if(c.hidden){
51961             this.hide();
51962         }
51963     },
51964     /**
51965      * Returns true if this region is currently visible.
51966      * @return {Boolean}
51967      */
51968     isVisible : function(){
51969         return this.visible;
51970     },
51971
51972     /**
51973      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51974      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51975      */
51976     setCollapsedTitle : function(title){
51977         title = title || "&#160;";
51978         if(this.collapsedTitleTextEl){
51979             this.collapsedTitleTextEl.innerHTML = title;
51980         }
51981     },
51982
51983     getBox : function(){
51984         var b;
51985         if(!this.collapsed){
51986             b = this.el.getBox(false, true);
51987         }else{
51988             b = this.collapsedEl.getBox(false, true);
51989         }
51990         return b;
51991     },
51992
51993     getMargins : function(){
51994         return this.collapsed ? this.cmargins : this.margins;
51995     },
51996
51997     highlight : function(){
51998         this.el.addClass("x-layout-panel-dragover");
51999     },
52000
52001     unhighlight : function(){
52002         this.el.removeClass("x-layout-panel-dragover");
52003     },
52004
52005     updateBox : function(box){
52006         this.box = box;
52007         if(!this.collapsed){
52008             this.el.dom.style.left = box.x + "px";
52009             this.el.dom.style.top = box.y + "px";
52010             this.updateBody(box.width, box.height);
52011         }else{
52012             this.collapsedEl.dom.style.left = box.x + "px";
52013             this.collapsedEl.dom.style.top = box.y + "px";
52014             this.collapsedEl.setSize(box.width, box.height);
52015         }
52016         if(this.tabs){
52017             this.tabs.autoSizeTabs();
52018         }
52019     },
52020
52021     updateBody : function(w, h){
52022         if(w !== null){
52023             this.el.setWidth(w);
52024             w -= this.el.getBorderWidth("rl");
52025             if(this.config.adjustments){
52026                 w += this.config.adjustments[0];
52027             }
52028         }
52029         if(h !== null){
52030             this.el.setHeight(h);
52031             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52032             h -= this.el.getBorderWidth("tb");
52033             if(this.config.adjustments){
52034                 h += this.config.adjustments[1];
52035             }
52036             this.bodyEl.setHeight(h);
52037             if(this.tabs){
52038                 h = this.tabs.syncHeight(h);
52039             }
52040         }
52041         if(this.panelSize){
52042             w = w !== null ? w : this.panelSize.width;
52043             h = h !== null ? h : this.panelSize.height;
52044         }
52045         if(this.activePanel){
52046             var el = this.activePanel.getEl();
52047             w = w !== null ? w : el.getWidth();
52048             h = h !== null ? h : el.getHeight();
52049             this.panelSize = {width: w, height: h};
52050             this.activePanel.setSize(w, h);
52051         }
52052         if(Roo.isIE && this.tabs){
52053             this.tabs.el.repaint();
52054         }
52055     },
52056
52057     /**
52058      * Returns the container element for this region.
52059      * @return {Roo.Element}
52060      */
52061     getEl : function(){
52062         return this.el;
52063     },
52064
52065     /**
52066      * Hides this region.
52067      */
52068     hide : function(){
52069         if(!this.collapsed){
52070             this.el.dom.style.left = "-2000px";
52071             this.el.hide();
52072         }else{
52073             this.collapsedEl.dom.style.left = "-2000px";
52074             this.collapsedEl.hide();
52075         }
52076         this.visible = false;
52077         this.fireEvent("visibilitychange", this, false);
52078     },
52079
52080     /**
52081      * Shows this region if it was previously hidden.
52082      */
52083     show : function(){
52084         if(!this.collapsed){
52085             this.el.show();
52086         }else{
52087             this.collapsedEl.show();
52088         }
52089         this.visible = true;
52090         this.fireEvent("visibilitychange", this, true);
52091     },
52092
52093     closeClicked : function(){
52094         if(this.activePanel){
52095             this.remove(this.activePanel);
52096         }
52097     },
52098
52099     collapseClick : function(e){
52100         if(this.isSlid){
52101            e.stopPropagation();
52102            this.slideIn();
52103         }else{
52104            e.stopPropagation();
52105            this.slideOut();
52106         }
52107     },
52108
52109     /**
52110      * Collapses this region.
52111      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52112      */
52113     collapse : function(skipAnim, skipCheck = false){
52114         if(this.collapsed) {
52115             return;
52116         }
52117         
52118         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52119             
52120             this.collapsed = true;
52121             if(this.split){
52122                 this.split.el.hide();
52123             }
52124             if(this.config.animate && skipAnim !== true){
52125                 this.fireEvent("invalidated", this);
52126                 this.animateCollapse();
52127             }else{
52128                 this.el.setLocation(-20000,-20000);
52129                 this.el.hide();
52130                 this.collapsedEl.show();
52131                 this.fireEvent("collapsed", this);
52132                 this.fireEvent("invalidated", this);
52133             }
52134         }
52135         
52136     },
52137
52138     animateCollapse : function(){
52139         // overridden
52140     },
52141
52142     /**
52143      * Expands this region if it was previously collapsed.
52144      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52145      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52146      */
52147     expand : function(e, skipAnim){
52148         if(e) {
52149             e.stopPropagation();
52150         }
52151         if(!this.collapsed || this.el.hasActiveFx()) {
52152             return;
52153         }
52154         if(this.isSlid){
52155             this.afterSlideIn();
52156             skipAnim = true;
52157         }
52158         this.collapsed = false;
52159         if(this.config.animate && skipAnim !== true){
52160             this.animateExpand();
52161         }else{
52162             this.el.show();
52163             if(this.split){
52164                 this.split.el.show();
52165             }
52166             this.collapsedEl.setLocation(-2000,-2000);
52167             this.collapsedEl.hide();
52168             this.fireEvent("invalidated", this);
52169             this.fireEvent("expanded", this);
52170         }
52171     },
52172
52173     animateExpand : function(){
52174         // overridden
52175     },
52176
52177     initTabs : function()
52178     {
52179         this.bodyEl.setStyle("overflow", "hidden");
52180         var ts = new Roo.TabPanel(
52181                 this.bodyEl.dom,
52182                 {
52183                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52184                     disableTooltips: this.config.disableTabTips,
52185                     toolbar : this.config.toolbar
52186                 }
52187         );
52188         if(this.config.hideTabs){
52189             ts.stripWrap.setDisplayed(false);
52190         }
52191         this.tabs = ts;
52192         ts.resizeTabs = this.config.resizeTabs === true;
52193         ts.minTabWidth = this.config.minTabWidth || 40;
52194         ts.maxTabWidth = this.config.maxTabWidth || 250;
52195         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52196         ts.monitorResize = false;
52197         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52198         ts.bodyEl.addClass('x-layout-tabs-body');
52199         this.panels.each(this.initPanelAsTab, this);
52200     },
52201
52202     initPanelAsTab : function(panel){
52203         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52204                     this.config.closeOnTab && panel.isClosable());
52205         if(panel.tabTip !== undefined){
52206             ti.setTooltip(panel.tabTip);
52207         }
52208         ti.on("activate", function(){
52209               this.setActivePanel(panel);
52210         }, this);
52211         if(this.config.closeOnTab){
52212             ti.on("beforeclose", function(t, e){
52213                 e.cancel = true;
52214                 this.remove(panel);
52215             }, this);
52216         }
52217         return ti;
52218     },
52219
52220     updatePanelTitle : function(panel, title){
52221         if(this.activePanel == panel){
52222             this.updateTitle(title);
52223         }
52224         if(this.tabs){
52225             var ti = this.tabs.getTab(panel.getEl().id);
52226             ti.setText(title);
52227             if(panel.tabTip !== undefined){
52228                 ti.setTooltip(panel.tabTip);
52229             }
52230         }
52231     },
52232
52233     updateTitle : function(title){
52234         if(this.titleTextEl && !this.config.title){
52235             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52236         }
52237     },
52238
52239     setActivePanel : function(panel){
52240         panel = this.getPanel(panel);
52241         if(this.activePanel && this.activePanel != panel){
52242             this.activePanel.setActiveState(false);
52243         }
52244         this.activePanel = panel;
52245         panel.setActiveState(true);
52246         if(this.panelSize){
52247             panel.setSize(this.panelSize.width, this.panelSize.height);
52248         }
52249         if(this.closeBtn){
52250             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52251         }
52252         this.updateTitle(panel.getTitle());
52253         if(this.tabs){
52254             this.fireEvent("invalidated", this);
52255         }
52256         this.fireEvent("panelactivated", this, panel);
52257     },
52258
52259     /**
52260      * Shows the specified panel.
52261      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52262      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52263      */
52264     showPanel : function(panel)
52265     {
52266         panel = this.getPanel(panel);
52267         if(panel){
52268             if(this.tabs){
52269                 var tab = this.tabs.getTab(panel.getEl().id);
52270                 if(tab.isHidden()){
52271                     this.tabs.unhideTab(tab.id);
52272                 }
52273                 tab.activate();
52274             }else{
52275                 this.setActivePanel(panel);
52276             }
52277         }
52278         return panel;
52279     },
52280
52281     /**
52282      * Get the active panel for this region.
52283      * @return {Roo.ContentPanel} The active panel or null
52284      */
52285     getActivePanel : function(){
52286         return this.activePanel;
52287     },
52288
52289     validateVisibility : function(){
52290         if(this.panels.getCount() < 1){
52291             this.updateTitle("&#160;");
52292             this.closeBtn.hide();
52293             this.hide();
52294         }else{
52295             if(!this.isVisible()){
52296                 this.show();
52297             }
52298         }
52299     },
52300
52301     /**
52302      * Adds the passed ContentPanel(s) to this region.
52303      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52304      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52305      */
52306     add : function(panel){
52307         if(arguments.length > 1){
52308             for(var i = 0, len = arguments.length; i < len; i++) {
52309                 this.add(arguments[i]);
52310             }
52311             return null;
52312         }
52313         if(this.hasPanel(panel)){
52314             this.showPanel(panel);
52315             return panel;
52316         }
52317         panel.setRegion(this);
52318         this.panels.add(panel);
52319         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52320             this.bodyEl.dom.appendChild(panel.getEl().dom);
52321             if(panel.background !== true){
52322                 this.setActivePanel(panel);
52323             }
52324             this.fireEvent("paneladded", this, panel);
52325             return panel;
52326         }
52327         if(!this.tabs){
52328             this.initTabs();
52329         }else{
52330             this.initPanelAsTab(panel);
52331         }
52332         if(panel.background !== true){
52333             this.tabs.activate(panel.getEl().id);
52334         }
52335         this.fireEvent("paneladded", this, panel);
52336         return panel;
52337     },
52338
52339     /**
52340      * Hides the tab for the specified panel.
52341      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52342      */
52343     hidePanel : function(panel){
52344         if(this.tabs && (panel = this.getPanel(panel))){
52345             this.tabs.hideTab(panel.getEl().id);
52346         }
52347     },
52348
52349     /**
52350      * Unhides the tab for a previously hidden panel.
52351      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52352      */
52353     unhidePanel : function(panel){
52354         if(this.tabs && (panel = this.getPanel(panel))){
52355             this.tabs.unhideTab(panel.getEl().id);
52356         }
52357     },
52358
52359     clearPanels : function(){
52360         while(this.panels.getCount() > 0){
52361              this.remove(this.panels.first());
52362         }
52363     },
52364
52365     /**
52366      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52367      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52368      * @param {Boolean} preservePanel Overrides the config preservePanel option
52369      * @return {Roo.ContentPanel} The panel that was removed
52370      */
52371     remove : function(panel, preservePanel){
52372         panel = this.getPanel(panel);
52373         if(!panel){
52374             return null;
52375         }
52376         var e = {};
52377         this.fireEvent("beforeremove", this, panel, e);
52378         if(e.cancel === true){
52379             return null;
52380         }
52381         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52382         var panelId = panel.getId();
52383         this.panels.removeKey(panelId);
52384         if(preservePanel){
52385             document.body.appendChild(panel.getEl().dom);
52386         }
52387         if(this.tabs){
52388             this.tabs.removeTab(panel.getEl().id);
52389         }else if (!preservePanel){
52390             this.bodyEl.dom.removeChild(panel.getEl().dom);
52391         }
52392         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52393             var p = this.panels.first();
52394             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52395             tempEl.appendChild(p.getEl().dom);
52396             this.bodyEl.update("");
52397             this.bodyEl.dom.appendChild(p.getEl().dom);
52398             tempEl = null;
52399             this.updateTitle(p.getTitle());
52400             this.tabs = null;
52401             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52402             this.setActivePanel(p);
52403         }
52404         panel.setRegion(null);
52405         if(this.activePanel == panel){
52406             this.activePanel = null;
52407         }
52408         if(this.config.autoDestroy !== false && preservePanel !== true){
52409             try{panel.destroy();}catch(e){}
52410         }
52411         this.fireEvent("panelremoved", this, panel);
52412         return panel;
52413     },
52414
52415     /**
52416      * Returns the TabPanel component used by this region
52417      * @return {Roo.TabPanel}
52418      */
52419     getTabs : function(){
52420         return this.tabs;
52421     },
52422
52423     createTool : function(parentEl, className){
52424         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52425             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52426         btn.addClassOnOver("x-layout-tools-button-over");
52427         return btn;
52428     }
52429 });/*
52430  * Based on:
52431  * Ext JS Library 1.1.1
52432  * Copyright(c) 2006-2007, Ext JS, LLC.
52433  *
52434  * Originally Released Under LGPL - original licence link has changed is not relivant.
52435  *
52436  * Fork - LGPL
52437  * <script type="text/javascript">
52438  */
52439  
52440
52441
52442 /**
52443  * @class Roo.SplitLayoutRegion
52444  * @extends Roo.LayoutRegion
52445  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52446  */
52447 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52448     this.cursor = cursor;
52449     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52450 };
52451
52452 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52453     splitTip : "Drag to resize.",
52454     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52455     useSplitTips : false,
52456
52457     applyConfig : function(config){
52458         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52459         if(config.split){
52460             if(!this.split){
52461                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52462                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52463                 /** The SplitBar for this region 
52464                 * @type Roo.SplitBar */
52465                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52466                 this.split.on("moved", this.onSplitMove, this);
52467                 this.split.useShim = config.useShim === true;
52468                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52469                 if(this.useSplitTips){
52470                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52471                 }
52472                 if(config.collapsible){
52473                     this.split.el.on("dblclick", this.collapse,  this);
52474                 }
52475             }
52476             if(typeof config.minSize != "undefined"){
52477                 this.split.minSize = config.minSize;
52478             }
52479             if(typeof config.maxSize != "undefined"){
52480                 this.split.maxSize = config.maxSize;
52481             }
52482             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52483                 this.hideSplitter();
52484             }
52485         }
52486     },
52487
52488     getHMaxSize : function(){
52489          var cmax = this.config.maxSize || 10000;
52490          var center = this.mgr.getRegion("center");
52491          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52492     },
52493
52494     getVMaxSize : function(){
52495          var cmax = this.config.maxSize || 10000;
52496          var center = this.mgr.getRegion("center");
52497          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52498     },
52499
52500     onSplitMove : function(split, newSize){
52501         this.fireEvent("resized", this, newSize);
52502     },
52503     
52504     /** 
52505      * Returns the {@link Roo.SplitBar} for this region.
52506      * @return {Roo.SplitBar}
52507      */
52508     getSplitBar : function(){
52509         return this.split;
52510     },
52511     
52512     hide : function(){
52513         this.hideSplitter();
52514         Roo.SplitLayoutRegion.superclass.hide.call(this);
52515     },
52516
52517     hideSplitter : function(){
52518         if(this.split){
52519             this.split.el.setLocation(-2000,-2000);
52520             this.split.el.hide();
52521         }
52522     },
52523
52524     show : function(){
52525         if(this.split){
52526             this.split.el.show();
52527         }
52528         Roo.SplitLayoutRegion.superclass.show.call(this);
52529     },
52530     
52531     beforeSlide: function(){
52532         if(Roo.isGecko){// firefox overflow auto bug workaround
52533             this.bodyEl.clip();
52534             if(this.tabs) {
52535                 this.tabs.bodyEl.clip();
52536             }
52537             if(this.activePanel){
52538                 this.activePanel.getEl().clip();
52539                 
52540                 if(this.activePanel.beforeSlide){
52541                     this.activePanel.beforeSlide();
52542                 }
52543             }
52544         }
52545     },
52546     
52547     afterSlide : function(){
52548         if(Roo.isGecko){// firefox overflow auto bug workaround
52549             this.bodyEl.unclip();
52550             if(this.tabs) {
52551                 this.tabs.bodyEl.unclip();
52552             }
52553             if(this.activePanel){
52554                 this.activePanel.getEl().unclip();
52555                 if(this.activePanel.afterSlide){
52556                     this.activePanel.afterSlide();
52557                 }
52558             }
52559         }
52560     },
52561
52562     initAutoHide : function(){
52563         if(this.autoHide !== false){
52564             if(!this.autoHideHd){
52565                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52566                 this.autoHideHd = {
52567                     "mouseout": function(e){
52568                         if(!e.within(this.el, true)){
52569                             st.delay(500);
52570                         }
52571                     },
52572                     "mouseover" : function(e){
52573                         st.cancel();
52574                     },
52575                     scope : this
52576                 };
52577             }
52578             this.el.on(this.autoHideHd);
52579         }
52580     },
52581
52582     clearAutoHide : function(){
52583         if(this.autoHide !== false){
52584             this.el.un("mouseout", this.autoHideHd.mouseout);
52585             this.el.un("mouseover", this.autoHideHd.mouseover);
52586         }
52587     },
52588
52589     clearMonitor : function(){
52590         Roo.get(document).un("click", this.slideInIf, this);
52591     },
52592
52593     // these names are backwards but not changed for compat
52594     slideOut : function(){
52595         if(this.isSlid || this.el.hasActiveFx()){
52596             return;
52597         }
52598         this.isSlid = true;
52599         if(this.collapseBtn){
52600             this.collapseBtn.hide();
52601         }
52602         this.closeBtnState = this.closeBtn.getStyle('display');
52603         this.closeBtn.hide();
52604         if(this.stickBtn){
52605             this.stickBtn.show();
52606         }
52607         this.el.show();
52608         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52609         this.beforeSlide();
52610         this.el.setStyle("z-index", 10001);
52611         this.el.slideIn(this.getSlideAnchor(), {
52612             callback: function(){
52613                 this.afterSlide();
52614                 this.initAutoHide();
52615                 Roo.get(document).on("click", this.slideInIf, this);
52616                 this.fireEvent("slideshow", this);
52617             },
52618             scope: this,
52619             block: true
52620         });
52621     },
52622
52623     afterSlideIn : function(){
52624         this.clearAutoHide();
52625         this.isSlid = false;
52626         this.clearMonitor();
52627         this.el.setStyle("z-index", "");
52628         if(this.collapseBtn){
52629             this.collapseBtn.show();
52630         }
52631         this.closeBtn.setStyle('display', this.closeBtnState);
52632         if(this.stickBtn){
52633             this.stickBtn.hide();
52634         }
52635         this.fireEvent("slidehide", this);
52636     },
52637
52638     slideIn : function(cb){
52639         if(!this.isSlid || this.el.hasActiveFx()){
52640             Roo.callback(cb);
52641             return;
52642         }
52643         this.isSlid = false;
52644         this.beforeSlide();
52645         this.el.slideOut(this.getSlideAnchor(), {
52646             callback: function(){
52647                 this.el.setLeftTop(-10000, -10000);
52648                 this.afterSlide();
52649                 this.afterSlideIn();
52650                 Roo.callback(cb);
52651             },
52652             scope: this,
52653             block: true
52654         });
52655     },
52656     
52657     slideInIf : function(e){
52658         if(!e.within(this.el)){
52659             this.slideIn();
52660         }
52661     },
52662
52663     animateCollapse : function(){
52664         this.beforeSlide();
52665         this.el.setStyle("z-index", 20000);
52666         var anchor = this.getSlideAnchor();
52667         this.el.slideOut(anchor, {
52668             callback : function(){
52669                 this.el.setStyle("z-index", "");
52670                 this.collapsedEl.slideIn(anchor, {duration:.3});
52671                 this.afterSlide();
52672                 this.el.setLocation(-10000,-10000);
52673                 this.el.hide();
52674                 this.fireEvent("collapsed", this);
52675             },
52676             scope: this,
52677             block: true
52678         });
52679     },
52680
52681     animateExpand : function(){
52682         this.beforeSlide();
52683         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52684         this.el.setStyle("z-index", 20000);
52685         this.collapsedEl.hide({
52686             duration:.1
52687         });
52688         this.el.slideIn(this.getSlideAnchor(), {
52689             callback : function(){
52690                 this.el.setStyle("z-index", "");
52691                 this.afterSlide();
52692                 if(this.split){
52693                     this.split.el.show();
52694                 }
52695                 this.fireEvent("invalidated", this);
52696                 this.fireEvent("expanded", this);
52697             },
52698             scope: this,
52699             block: true
52700         });
52701     },
52702
52703     anchors : {
52704         "west" : "left",
52705         "east" : "right",
52706         "north" : "top",
52707         "south" : "bottom"
52708     },
52709
52710     sanchors : {
52711         "west" : "l",
52712         "east" : "r",
52713         "north" : "t",
52714         "south" : "b"
52715     },
52716
52717     canchors : {
52718         "west" : "tl-tr",
52719         "east" : "tr-tl",
52720         "north" : "tl-bl",
52721         "south" : "bl-tl"
52722     },
52723
52724     getAnchor : function(){
52725         return this.anchors[this.position];
52726     },
52727
52728     getCollapseAnchor : function(){
52729         return this.canchors[this.position];
52730     },
52731
52732     getSlideAnchor : function(){
52733         return this.sanchors[this.position];
52734     },
52735
52736     getAlignAdj : function(){
52737         var cm = this.cmargins;
52738         switch(this.position){
52739             case "west":
52740                 return [0, 0];
52741             break;
52742             case "east":
52743                 return [0, 0];
52744             break;
52745             case "north":
52746                 return [0, 0];
52747             break;
52748             case "south":
52749                 return [0, 0];
52750             break;
52751         }
52752     },
52753
52754     getExpandAdj : function(){
52755         var c = this.collapsedEl, cm = this.cmargins;
52756         switch(this.position){
52757             case "west":
52758                 return [-(cm.right+c.getWidth()+cm.left), 0];
52759             break;
52760             case "east":
52761                 return [cm.right+c.getWidth()+cm.left, 0];
52762             break;
52763             case "north":
52764                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52765             break;
52766             case "south":
52767                 return [0, cm.top+cm.bottom+c.getHeight()];
52768             break;
52769         }
52770     }
52771 });/*
52772  * Based on:
52773  * Ext JS Library 1.1.1
52774  * Copyright(c) 2006-2007, Ext JS, LLC.
52775  *
52776  * Originally Released Under LGPL - original licence link has changed is not relivant.
52777  *
52778  * Fork - LGPL
52779  * <script type="text/javascript">
52780  */
52781 /*
52782  * These classes are private internal classes
52783  */
52784 Roo.CenterLayoutRegion = function(mgr, config){
52785     Roo.LayoutRegion.call(this, mgr, config, "center");
52786     this.visible = true;
52787     this.minWidth = config.minWidth || 20;
52788     this.minHeight = config.minHeight || 20;
52789 };
52790
52791 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52792     hide : function(){
52793         // center panel can't be hidden
52794     },
52795     
52796     show : function(){
52797         // center panel can't be hidden
52798     },
52799     
52800     getMinWidth: function(){
52801         return this.minWidth;
52802     },
52803     
52804     getMinHeight: function(){
52805         return this.minHeight;
52806     }
52807 });
52808
52809
52810 Roo.NorthLayoutRegion = function(mgr, config){
52811     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52812     if(this.split){
52813         this.split.placement = Roo.SplitBar.TOP;
52814         this.split.orientation = Roo.SplitBar.VERTICAL;
52815         this.split.el.addClass("x-layout-split-v");
52816     }
52817     var size = config.initialSize || config.height;
52818     if(typeof size != "undefined"){
52819         this.el.setHeight(size);
52820     }
52821 };
52822 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52823     orientation: Roo.SplitBar.VERTICAL,
52824     getBox : function(){
52825         if(this.collapsed){
52826             return this.collapsedEl.getBox();
52827         }
52828         var box = this.el.getBox();
52829         if(this.split){
52830             box.height += this.split.el.getHeight();
52831         }
52832         return box;
52833     },
52834     
52835     updateBox : function(box){
52836         if(this.split && !this.collapsed){
52837             box.height -= this.split.el.getHeight();
52838             this.split.el.setLeft(box.x);
52839             this.split.el.setTop(box.y+box.height);
52840             this.split.el.setWidth(box.width);
52841         }
52842         if(this.collapsed){
52843             this.updateBody(box.width, null);
52844         }
52845         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52846     }
52847 });
52848
52849 Roo.SouthLayoutRegion = function(mgr, config){
52850     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52851     if(this.split){
52852         this.split.placement = Roo.SplitBar.BOTTOM;
52853         this.split.orientation = Roo.SplitBar.VERTICAL;
52854         this.split.el.addClass("x-layout-split-v");
52855     }
52856     var size = config.initialSize || config.height;
52857     if(typeof size != "undefined"){
52858         this.el.setHeight(size);
52859     }
52860 };
52861 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52862     orientation: Roo.SplitBar.VERTICAL,
52863     getBox : function(){
52864         if(this.collapsed){
52865             return this.collapsedEl.getBox();
52866         }
52867         var box = this.el.getBox();
52868         if(this.split){
52869             var sh = this.split.el.getHeight();
52870             box.height += sh;
52871             box.y -= sh;
52872         }
52873         return box;
52874     },
52875     
52876     updateBox : function(box){
52877         if(this.split && !this.collapsed){
52878             var sh = this.split.el.getHeight();
52879             box.height -= sh;
52880             box.y += sh;
52881             this.split.el.setLeft(box.x);
52882             this.split.el.setTop(box.y-sh);
52883             this.split.el.setWidth(box.width);
52884         }
52885         if(this.collapsed){
52886             this.updateBody(box.width, null);
52887         }
52888         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52889     }
52890 });
52891
52892 Roo.EastLayoutRegion = function(mgr, config){
52893     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52894     if(this.split){
52895         this.split.placement = Roo.SplitBar.RIGHT;
52896         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52897         this.split.el.addClass("x-layout-split-h");
52898     }
52899     var size = config.initialSize || config.width;
52900     if(typeof size != "undefined"){
52901         this.el.setWidth(size);
52902     }
52903 };
52904 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52905     orientation: Roo.SplitBar.HORIZONTAL,
52906     getBox : function(){
52907         if(this.collapsed){
52908             return this.collapsedEl.getBox();
52909         }
52910         var box = this.el.getBox();
52911         if(this.split){
52912             var sw = this.split.el.getWidth();
52913             box.width += sw;
52914             box.x -= sw;
52915         }
52916         return box;
52917     },
52918
52919     updateBox : function(box){
52920         if(this.split && !this.collapsed){
52921             var sw = this.split.el.getWidth();
52922             box.width -= sw;
52923             this.split.el.setLeft(box.x);
52924             this.split.el.setTop(box.y);
52925             this.split.el.setHeight(box.height);
52926             box.x += sw;
52927         }
52928         if(this.collapsed){
52929             this.updateBody(null, box.height);
52930         }
52931         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52932     }
52933 });
52934
52935 Roo.WestLayoutRegion = function(mgr, config){
52936     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52937     if(this.split){
52938         this.split.placement = Roo.SplitBar.LEFT;
52939         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52940         this.split.el.addClass("x-layout-split-h");
52941     }
52942     var size = config.initialSize || config.width;
52943     if(typeof size != "undefined"){
52944         this.el.setWidth(size);
52945     }
52946 };
52947 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52948     orientation: Roo.SplitBar.HORIZONTAL,
52949     getBox : function(){
52950         if(this.collapsed){
52951             return this.collapsedEl.getBox();
52952         }
52953         var box = this.el.getBox();
52954         if(this.split){
52955             box.width += this.split.el.getWidth();
52956         }
52957         return box;
52958     },
52959     
52960     updateBox : function(box){
52961         if(this.split && !this.collapsed){
52962             var sw = this.split.el.getWidth();
52963             box.width -= sw;
52964             this.split.el.setLeft(box.x+box.width);
52965             this.split.el.setTop(box.y);
52966             this.split.el.setHeight(box.height);
52967         }
52968         if(this.collapsed){
52969             this.updateBody(null, box.height);
52970         }
52971         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52972     }
52973 });
52974 /*
52975  * Based on:
52976  * Ext JS Library 1.1.1
52977  * Copyright(c) 2006-2007, Ext JS, LLC.
52978  *
52979  * Originally Released Under LGPL - original licence link has changed is not relivant.
52980  *
52981  * Fork - LGPL
52982  * <script type="text/javascript">
52983  */
52984  
52985  
52986 /*
52987  * Private internal class for reading and applying state
52988  */
52989 Roo.LayoutStateManager = function(layout){
52990      // default empty state
52991      this.state = {
52992         north: {},
52993         south: {},
52994         east: {},
52995         west: {}       
52996     };
52997 };
52998
52999 Roo.LayoutStateManager.prototype = {
53000     init : function(layout, provider){
53001         this.provider = provider;
53002         var state = provider.get(layout.id+"-layout-state");
53003         if(state){
53004             var wasUpdating = layout.isUpdating();
53005             if(!wasUpdating){
53006                 layout.beginUpdate();
53007             }
53008             for(var key in state){
53009                 if(typeof state[key] != "function"){
53010                     var rstate = state[key];
53011                     var r = layout.getRegion(key);
53012                     if(r && rstate){
53013                         if(rstate.size){
53014                             r.resizeTo(rstate.size);
53015                         }
53016                         if(rstate.collapsed == true){
53017                             r.collapse(true);
53018                         }else{
53019                             r.expand(null, true);
53020                         }
53021                     }
53022                 }
53023             }
53024             if(!wasUpdating){
53025                 layout.endUpdate();
53026             }
53027             this.state = state; 
53028         }
53029         this.layout = layout;
53030         layout.on("regionresized", this.onRegionResized, this);
53031         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53032         layout.on("regionexpanded", this.onRegionExpanded, this);
53033     },
53034     
53035     storeState : function(){
53036         this.provider.set(this.layout.id+"-layout-state", this.state);
53037     },
53038     
53039     onRegionResized : function(region, newSize){
53040         this.state[region.getPosition()].size = newSize;
53041         this.storeState();
53042     },
53043     
53044     onRegionCollapsed : function(region){
53045         this.state[region.getPosition()].collapsed = true;
53046         this.storeState();
53047     },
53048     
53049     onRegionExpanded : function(region){
53050         this.state[region.getPosition()].collapsed = false;
53051         this.storeState();
53052     }
53053 };/*
53054  * Based on:
53055  * Ext JS Library 1.1.1
53056  * Copyright(c) 2006-2007, Ext JS, LLC.
53057  *
53058  * Originally Released Under LGPL - original licence link has changed is not relivant.
53059  *
53060  * Fork - LGPL
53061  * <script type="text/javascript">
53062  */
53063 /**
53064  * @class Roo.ContentPanel
53065  * @extends Roo.util.Observable
53066  * A basic ContentPanel element.
53067  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53068  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53069  * @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
53070  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53071  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53072  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53073  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53074  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53075  * @cfg {String} title          The title for this panel
53076  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53077  * @cfg {String} url            Calls {@link #setUrl} with this value
53078  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53079  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53080  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53081  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53082
53083  * @constructor
53084  * Create a new ContentPanel.
53085  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53086  * @param {String/Object} config A string to set only the title or a config object
53087  * @param {String} content (optional) Set the HTML content for this panel
53088  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53089  */
53090 Roo.ContentPanel = function(el, config, content){
53091     
53092      
53093     /*
53094     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53095         config = el;
53096         el = Roo.id();
53097     }
53098     if (config && config.parentLayout) { 
53099         el = config.parentLayout.el.createChild(); 
53100     }
53101     */
53102     if(el.autoCreate){ // xtype is available if this is called from factory
53103         config = el;
53104         el = Roo.id();
53105     }
53106     this.el = Roo.get(el);
53107     if(!this.el && config && config.autoCreate){
53108         if(typeof config.autoCreate == "object"){
53109             if(!config.autoCreate.id){
53110                 config.autoCreate.id = config.id||el;
53111             }
53112             this.el = Roo.DomHelper.append(document.body,
53113                         config.autoCreate, true);
53114         }else{
53115             this.el = Roo.DomHelper.append(document.body,
53116                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53117         }
53118     }
53119     this.closable = false;
53120     this.loaded = false;
53121     this.active = false;
53122     if(typeof config == "string"){
53123         this.title = config;
53124     }else{
53125         Roo.apply(this, config);
53126     }
53127     
53128     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53129         this.wrapEl = this.el.wrap();
53130         this.toolbar.container = this.el.insertSibling(false, 'before');
53131         this.toolbar = new Roo.Toolbar(this.toolbar);
53132     }
53133     
53134     // xtype created footer. - not sure if will work as we normally have to render first..
53135     if (this.footer && !this.footer.el && this.footer.xtype) {
53136         if (!this.wrapEl) {
53137             this.wrapEl = this.el.wrap();
53138         }
53139     
53140         this.footer.container = this.wrapEl.createChild();
53141          
53142         this.footer = Roo.factory(this.footer, Roo);
53143         
53144     }
53145     
53146     if(this.resizeEl){
53147         this.resizeEl = Roo.get(this.resizeEl, true);
53148     }else{
53149         this.resizeEl = this.el;
53150     }
53151     // handle view.xtype
53152     
53153  
53154     
53155     
53156     this.addEvents({
53157         /**
53158          * @event activate
53159          * Fires when this panel is activated. 
53160          * @param {Roo.ContentPanel} this
53161          */
53162         "activate" : true,
53163         /**
53164          * @event deactivate
53165          * Fires when this panel is activated. 
53166          * @param {Roo.ContentPanel} this
53167          */
53168         "deactivate" : true,
53169
53170         /**
53171          * @event resize
53172          * Fires when this panel is resized if fitToFrame is true.
53173          * @param {Roo.ContentPanel} this
53174          * @param {Number} width The width after any component adjustments
53175          * @param {Number} height The height after any component adjustments
53176          */
53177         "resize" : true,
53178         
53179          /**
53180          * @event render
53181          * Fires when this tab is created
53182          * @param {Roo.ContentPanel} this
53183          */
53184         "render" : true
53185         
53186         
53187         
53188     });
53189     
53190
53191     
53192     
53193     if(this.autoScroll){
53194         this.resizeEl.setStyle("overflow", "auto");
53195     } else {
53196         // fix randome scrolling
53197         this.el.on('scroll', function() {
53198             Roo.log('fix random scolling');
53199             this.scrollTo('top',0); 
53200         });
53201     }
53202     content = content || this.content;
53203     if(content){
53204         this.setContent(content);
53205     }
53206     if(config && config.url){
53207         this.setUrl(this.url, this.params, this.loadOnce);
53208     }
53209     
53210     
53211     
53212     Roo.ContentPanel.superclass.constructor.call(this);
53213     
53214     if (this.view && typeof(this.view.xtype) != 'undefined') {
53215         this.view.el = this.el.appendChild(document.createElement("div"));
53216         this.view = Roo.factory(this.view); 
53217         this.view.render  &&  this.view.render(false, '');  
53218     }
53219     
53220     
53221     this.fireEvent('render', this);
53222 };
53223
53224 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53225     tabTip:'',
53226     setRegion : function(region){
53227         this.region = region;
53228         if(region){
53229            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53230         }else{
53231            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53232         } 
53233     },
53234     
53235     /**
53236      * Returns the toolbar for this Panel if one was configured. 
53237      * @return {Roo.Toolbar} 
53238      */
53239     getToolbar : function(){
53240         return this.toolbar;
53241     },
53242     
53243     setActiveState : function(active){
53244         this.active = active;
53245         if(!active){
53246             this.fireEvent("deactivate", this);
53247         }else{
53248             this.fireEvent("activate", this);
53249         }
53250     },
53251     /**
53252      * Updates this panel's element
53253      * @param {String} content The new content
53254      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53255     */
53256     setContent : function(content, loadScripts){
53257         this.el.update(content, loadScripts);
53258     },
53259
53260     ignoreResize : function(w, h){
53261         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53262             return true;
53263         }else{
53264             this.lastSize = {width: w, height: h};
53265             return false;
53266         }
53267     },
53268     /**
53269      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53270      * @return {Roo.UpdateManager} The UpdateManager
53271      */
53272     getUpdateManager : function(){
53273         return this.el.getUpdateManager();
53274     },
53275      /**
53276      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53277      * @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:
53278 <pre><code>
53279 panel.load({
53280     url: "your-url.php",
53281     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53282     callback: yourFunction,
53283     scope: yourObject, //(optional scope)
53284     discardUrl: false,
53285     nocache: false,
53286     text: "Loading...",
53287     timeout: 30,
53288     scripts: false
53289 });
53290 </code></pre>
53291      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53292      * 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.
53293      * @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}
53294      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53295      * @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.
53296      * @return {Roo.ContentPanel} this
53297      */
53298     load : function(){
53299         var um = this.el.getUpdateManager();
53300         um.update.apply(um, arguments);
53301         return this;
53302     },
53303
53304
53305     /**
53306      * 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.
53307      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53308      * @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)
53309      * @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)
53310      * @return {Roo.UpdateManager} The UpdateManager
53311      */
53312     setUrl : function(url, params, loadOnce){
53313         if(this.refreshDelegate){
53314             this.removeListener("activate", this.refreshDelegate);
53315         }
53316         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53317         this.on("activate", this.refreshDelegate);
53318         return this.el.getUpdateManager();
53319     },
53320     
53321     _handleRefresh : function(url, params, loadOnce){
53322         if(!loadOnce || !this.loaded){
53323             var updater = this.el.getUpdateManager();
53324             updater.update(url, params, this._setLoaded.createDelegate(this));
53325         }
53326     },
53327     
53328     _setLoaded : function(){
53329         this.loaded = true;
53330     }, 
53331     
53332     /**
53333      * Returns this panel's id
53334      * @return {String} 
53335      */
53336     getId : function(){
53337         return this.el.id;
53338     },
53339     
53340     /** 
53341      * Returns this panel's element - used by regiosn to add.
53342      * @return {Roo.Element} 
53343      */
53344     getEl : function(){
53345         return this.wrapEl || this.el;
53346     },
53347     
53348     adjustForComponents : function(width, height)
53349     {
53350         //Roo.log('adjustForComponents ');
53351         if(this.resizeEl != this.el){
53352             width -= this.el.getFrameWidth('lr');
53353             height -= this.el.getFrameWidth('tb');
53354         }
53355         if(this.toolbar){
53356             var te = this.toolbar.getEl();
53357             height -= te.getHeight();
53358             te.setWidth(width);
53359         }
53360         if(this.footer){
53361             var te = this.footer.getEl();
53362             Roo.log("footer:" + te.getHeight());
53363             
53364             height -= te.getHeight();
53365             te.setWidth(width);
53366         }
53367         
53368         
53369         if(this.adjustments){
53370             width += this.adjustments[0];
53371             height += this.adjustments[1];
53372         }
53373         return {"width": width, "height": height};
53374     },
53375     
53376     setSize : function(width, height){
53377         if(this.fitToFrame && !this.ignoreResize(width, height)){
53378             if(this.fitContainer && this.resizeEl != this.el){
53379                 this.el.setSize(width, height);
53380             }
53381             var size = this.adjustForComponents(width, height);
53382             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53383             this.fireEvent('resize', this, size.width, size.height);
53384         }
53385     },
53386     
53387     /**
53388      * Returns this panel's title
53389      * @return {String} 
53390      */
53391     getTitle : function(){
53392         return this.title;
53393     },
53394     
53395     /**
53396      * Set this panel's title
53397      * @param {String} title
53398      */
53399     setTitle : function(title){
53400         this.title = title;
53401         if(this.region){
53402             this.region.updatePanelTitle(this, title);
53403         }
53404     },
53405     
53406     /**
53407      * Returns true is this panel was configured to be closable
53408      * @return {Boolean} 
53409      */
53410     isClosable : function(){
53411         return this.closable;
53412     },
53413     
53414     beforeSlide : function(){
53415         this.el.clip();
53416         this.resizeEl.clip();
53417     },
53418     
53419     afterSlide : function(){
53420         this.el.unclip();
53421         this.resizeEl.unclip();
53422     },
53423     
53424     /**
53425      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53426      *   Will fail silently if the {@link #setUrl} method has not been called.
53427      *   This does not activate the panel, just updates its content.
53428      */
53429     refresh : function(){
53430         if(this.refreshDelegate){
53431            this.loaded = false;
53432            this.refreshDelegate();
53433         }
53434     },
53435     
53436     /**
53437      * Destroys this panel
53438      */
53439     destroy : function(){
53440         this.el.removeAllListeners();
53441         var tempEl = document.createElement("span");
53442         tempEl.appendChild(this.el.dom);
53443         tempEl.innerHTML = "";
53444         this.el.remove();
53445         this.el = null;
53446     },
53447     
53448     /**
53449      * form - if the content panel contains a form - this is a reference to it.
53450      * @type {Roo.form.Form}
53451      */
53452     form : false,
53453     /**
53454      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53455      *    This contains a reference to it.
53456      * @type {Roo.View}
53457      */
53458     view : false,
53459     
53460       /**
53461      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53462      * <pre><code>
53463
53464 layout.addxtype({
53465        xtype : 'Form',
53466        items: [ .... ]
53467    }
53468 );
53469
53470 </code></pre>
53471      * @param {Object} cfg Xtype definition of item to add.
53472      */
53473     
53474     addxtype : function(cfg) {
53475         // add form..
53476         if (cfg.xtype.match(/^Form$/)) {
53477             
53478             var el;
53479             //if (this.footer) {
53480             //    el = this.footer.container.insertSibling(false, 'before');
53481             //} else {
53482                 el = this.el.createChild();
53483             //}
53484
53485             this.form = new  Roo.form.Form(cfg);
53486             
53487             
53488             if ( this.form.allItems.length) {
53489                 this.form.render(el.dom);
53490             }
53491             return this.form;
53492         }
53493         // should only have one of theses..
53494         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53495             // views.. should not be just added - used named prop 'view''
53496             
53497             cfg.el = this.el.appendChild(document.createElement("div"));
53498             // factory?
53499             
53500             var ret = new Roo.factory(cfg);
53501              
53502              ret.render && ret.render(false, ''); // render blank..
53503             this.view = ret;
53504             return ret;
53505         }
53506         return false;
53507     }
53508 });
53509
53510 /**
53511  * @class Roo.GridPanel
53512  * @extends Roo.ContentPanel
53513  * @constructor
53514  * Create a new GridPanel.
53515  * @param {Roo.grid.Grid} grid The grid for this panel
53516  * @param {String/Object} config A string to set only the panel's title, or a config object
53517  */
53518 Roo.GridPanel = function(grid, config){
53519     
53520   
53521     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53522         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53523         
53524     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53525     
53526     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53527     
53528     if(this.toolbar){
53529         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53530     }
53531     // xtype created footer. - not sure if will work as we normally have to render first..
53532     if (this.footer && !this.footer.el && this.footer.xtype) {
53533         
53534         this.footer.container = this.grid.getView().getFooterPanel(true);
53535         this.footer.dataSource = this.grid.dataSource;
53536         this.footer = Roo.factory(this.footer, Roo);
53537         
53538     }
53539     
53540     grid.monitorWindowResize = false; // turn off autosizing
53541     grid.autoHeight = false;
53542     grid.autoWidth = false;
53543     this.grid = grid;
53544     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53545 };
53546
53547 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53548     getId : function(){
53549         return this.grid.id;
53550     },
53551     
53552     /**
53553      * Returns the grid for this panel
53554      * @return {Roo.grid.Grid} 
53555      */
53556     getGrid : function(){
53557         return this.grid;    
53558     },
53559     
53560     setSize : function(width, height){
53561         if(!this.ignoreResize(width, height)){
53562             var grid = this.grid;
53563             var size = this.adjustForComponents(width, height);
53564             grid.getGridEl().setSize(size.width, size.height);
53565             grid.autoSize();
53566         }
53567     },
53568     
53569     beforeSlide : function(){
53570         this.grid.getView().scroller.clip();
53571     },
53572     
53573     afterSlide : function(){
53574         this.grid.getView().scroller.unclip();
53575     },
53576     
53577     destroy : function(){
53578         this.grid.destroy();
53579         delete this.grid;
53580         Roo.GridPanel.superclass.destroy.call(this); 
53581     }
53582 });
53583
53584
53585 /**
53586  * @class Roo.NestedLayoutPanel
53587  * @extends Roo.ContentPanel
53588  * @constructor
53589  * Create a new NestedLayoutPanel.
53590  * 
53591  * 
53592  * @param {Roo.BorderLayout} layout The layout for this panel
53593  * @param {String/Object} config A string to set only the title or a config object
53594  */
53595 Roo.NestedLayoutPanel = function(layout, config)
53596 {
53597     // construct with only one argument..
53598     /* FIXME - implement nicer consturctors
53599     if (layout.layout) {
53600         config = layout;
53601         layout = config.layout;
53602         delete config.layout;
53603     }
53604     if (layout.xtype && !layout.getEl) {
53605         // then layout needs constructing..
53606         layout = Roo.factory(layout, Roo);
53607     }
53608     */
53609     
53610     
53611     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53612     
53613     layout.monitorWindowResize = false; // turn off autosizing
53614     this.layout = layout;
53615     this.layout.getEl().addClass("x-layout-nested-layout");
53616     
53617     
53618     
53619     
53620 };
53621
53622 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53623
53624     setSize : function(width, height){
53625         if(!this.ignoreResize(width, height)){
53626             var size = this.adjustForComponents(width, height);
53627             var el = this.layout.getEl();
53628             el.setSize(size.width, size.height);
53629             var touch = el.dom.offsetWidth;
53630             this.layout.layout();
53631             // ie requires a double layout on the first pass
53632             if(Roo.isIE && !this.initialized){
53633                 this.initialized = true;
53634                 this.layout.layout();
53635             }
53636         }
53637     },
53638     
53639     // activate all subpanels if not currently active..
53640     
53641     setActiveState : function(active){
53642         this.active = active;
53643         if(!active){
53644             this.fireEvent("deactivate", this);
53645             return;
53646         }
53647         
53648         this.fireEvent("activate", this);
53649         // not sure if this should happen before or after..
53650         if (!this.layout) {
53651             return; // should not happen..
53652         }
53653         var reg = false;
53654         for (var r in this.layout.regions) {
53655             reg = this.layout.getRegion(r);
53656             if (reg.getActivePanel()) {
53657                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53658                 reg.setActivePanel(reg.getActivePanel());
53659                 continue;
53660             }
53661             if (!reg.panels.length) {
53662                 continue;
53663             }
53664             reg.showPanel(reg.getPanel(0));
53665         }
53666         
53667         
53668         
53669         
53670     },
53671     
53672     /**
53673      * Returns the nested BorderLayout for this panel
53674      * @return {Roo.BorderLayout} 
53675      */
53676     getLayout : function(){
53677         return this.layout;
53678     },
53679     
53680      /**
53681      * Adds a xtype elements to the layout of the nested panel
53682      * <pre><code>
53683
53684 panel.addxtype({
53685        xtype : 'ContentPanel',
53686        region: 'west',
53687        items: [ .... ]
53688    }
53689 );
53690
53691 panel.addxtype({
53692         xtype : 'NestedLayoutPanel',
53693         region: 'west',
53694         layout: {
53695            center: { },
53696            west: { }   
53697         },
53698         items : [ ... list of content panels or nested layout panels.. ]
53699    }
53700 );
53701 </code></pre>
53702      * @param {Object} cfg Xtype definition of item to add.
53703      */
53704     addxtype : function(cfg) {
53705         return this.layout.addxtype(cfg);
53706     
53707     }
53708 });
53709
53710 Roo.ScrollPanel = function(el, config, content){
53711     config = config || {};
53712     config.fitToFrame = true;
53713     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53714     
53715     this.el.dom.style.overflow = "hidden";
53716     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53717     this.el.removeClass("x-layout-inactive-content");
53718     this.el.on("mousewheel", this.onWheel, this);
53719
53720     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53721     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53722     up.unselectable(); down.unselectable();
53723     up.on("click", this.scrollUp, this);
53724     down.on("click", this.scrollDown, this);
53725     up.addClassOnOver("x-scroller-btn-over");
53726     down.addClassOnOver("x-scroller-btn-over");
53727     up.addClassOnClick("x-scroller-btn-click");
53728     down.addClassOnClick("x-scroller-btn-click");
53729     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53730
53731     this.resizeEl = this.el;
53732     this.el = wrap; this.up = up; this.down = down;
53733 };
53734
53735 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53736     increment : 100,
53737     wheelIncrement : 5,
53738     scrollUp : function(){
53739         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53740     },
53741
53742     scrollDown : function(){
53743         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53744     },
53745
53746     afterScroll : function(){
53747         var el = this.resizeEl;
53748         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53749         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53750         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53751     },
53752
53753     setSize : function(){
53754         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53755         this.afterScroll();
53756     },
53757
53758     onWheel : function(e){
53759         var d = e.getWheelDelta();
53760         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53761         this.afterScroll();
53762         e.stopEvent();
53763     },
53764
53765     setContent : function(content, loadScripts){
53766         this.resizeEl.update(content, loadScripts);
53767     }
53768
53769 });
53770
53771
53772
53773
53774
53775
53776
53777
53778
53779 /**
53780  * @class Roo.TreePanel
53781  * @extends Roo.ContentPanel
53782  * @constructor
53783  * Create a new TreePanel. - defaults to fit/scoll contents.
53784  * @param {String/Object} config A string to set only the panel's title, or a config object
53785  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53786  */
53787 Roo.TreePanel = function(config){
53788     var el = config.el;
53789     var tree = config.tree;
53790     delete config.tree; 
53791     delete config.el; // hopefull!
53792     
53793     // wrapper for IE7 strict & safari scroll issue
53794     
53795     var treeEl = el.createChild();
53796     config.resizeEl = treeEl;
53797     
53798     
53799     
53800     Roo.TreePanel.superclass.constructor.call(this, el, config);
53801  
53802  
53803     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53804     //console.log(tree);
53805     this.on('activate', function()
53806     {
53807         if (this.tree.rendered) {
53808             return;
53809         }
53810         //console.log('render tree');
53811         this.tree.render();
53812     });
53813     // this should not be needed.. - it's actually the 'el' that resizes?
53814     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53815     
53816     //this.on('resize',  function (cp, w, h) {
53817     //        this.tree.innerCt.setWidth(w);
53818     //        this.tree.innerCt.setHeight(h);
53819     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53820     //});
53821
53822         
53823     
53824 };
53825
53826 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53827     fitToFrame : true,
53828     autoScroll : true
53829 });
53830
53831
53832
53833
53834
53835
53836
53837
53838
53839
53840
53841 /*
53842  * Based on:
53843  * Ext JS Library 1.1.1
53844  * Copyright(c) 2006-2007, Ext JS, LLC.
53845  *
53846  * Originally Released Under LGPL - original licence link has changed is not relivant.
53847  *
53848  * Fork - LGPL
53849  * <script type="text/javascript">
53850  */
53851  
53852
53853 /**
53854  * @class Roo.ReaderLayout
53855  * @extends Roo.BorderLayout
53856  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53857  * center region containing two nested regions (a top one for a list view and one for item preview below),
53858  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53859  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53860  * expedites the setup of the overall layout and regions for this common application style.
53861  * Example:
53862  <pre><code>
53863 var reader = new Roo.ReaderLayout();
53864 var CP = Roo.ContentPanel;  // shortcut for adding
53865
53866 reader.beginUpdate();
53867 reader.add("north", new CP("north", "North"));
53868 reader.add("west", new CP("west", {title: "West"}));
53869 reader.add("east", new CP("east", {title: "East"}));
53870
53871 reader.regions.listView.add(new CP("listView", "List"));
53872 reader.regions.preview.add(new CP("preview", "Preview"));
53873 reader.endUpdate();
53874 </code></pre>
53875 * @constructor
53876 * Create a new ReaderLayout
53877 * @param {Object} config Configuration options
53878 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53879 * document.body if omitted)
53880 */
53881 Roo.ReaderLayout = function(config, renderTo){
53882     var c = config || {size:{}};
53883     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53884         north: c.north !== false ? Roo.apply({
53885             split:false,
53886             initialSize: 32,
53887             titlebar: false
53888         }, c.north) : false,
53889         west: c.west !== false ? Roo.apply({
53890             split:true,
53891             initialSize: 200,
53892             minSize: 175,
53893             maxSize: 400,
53894             titlebar: true,
53895             collapsible: true,
53896             animate: true,
53897             margins:{left:5,right:0,bottom:5,top:5},
53898             cmargins:{left:5,right:5,bottom:5,top:5}
53899         }, c.west) : false,
53900         east: c.east !== false ? Roo.apply({
53901             split:true,
53902             initialSize: 200,
53903             minSize: 175,
53904             maxSize: 400,
53905             titlebar: true,
53906             collapsible: true,
53907             animate: true,
53908             margins:{left:0,right:5,bottom:5,top:5},
53909             cmargins:{left:5,right:5,bottom:5,top:5}
53910         }, c.east) : false,
53911         center: Roo.apply({
53912             tabPosition: 'top',
53913             autoScroll:false,
53914             closeOnTab: true,
53915             titlebar:false,
53916             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53917         }, c.center)
53918     });
53919
53920     this.el.addClass('x-reader');
53921
53922     this.beginUpdate();
53923
53924     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53925         south: c.preview !== false ? Roo.apply({
53926             split:true,
53927             initialSize: 200,
53928             minSize: 100,
53929             autoScroll:true,
53930             collapsible:true,
53931             titlebar: true,
53932             cmargins:{top:5,left:0, right:0, bottom:0}
53933         }, c.preview) : false,
53934         center: Roo.apply({
53935             autoScroll:false,
53936             titlebar:false,
53937             minHeight:200
53938         }, c.listView)
53939     });
53940     this.add('center', new Roo.NestedLayoutPanel(inner,
53941             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53942
53943     this.endUpdate();
53944
53945     this.regions.preview = inner.getRegion('south');
53946     this.regions.listView = inner.getRegion('center');
53947 };
53948
53949 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53950  * Based on:
53951  * Ext JS Library 1.1.1
53952  * Copyright(c) 2006-2007, Ext JS, LLC.
53953  *
53954  * Originally Released Under LGPL - original licence link has changed is not relivant.
53955  *
53956  * Fork - LGPL
53957  * <script type="text/javascript">
53958  */
53959  
53960 /**
53961  * @class Roo.grid.Grid
53962  * @extends Roo.util.Observable
53963  * This class represents the primary interface of a component based grid control.
53964  * <br><br>Usage:<pre><code>
53965  var grid = new Roo.grid.Grid("my-container-id", {
53966      ds: myDataStore,
53967      cm: myColModel,
53968      selModel: mySelectionModel,
53969      autoSizeColumns: true,
53970      monitorWindowResize: false,
53971      trackMouseOver: true
53972  });
53973  // set any options
53974  grid.render();
53975  * </code></pre>
53976  * <b>Common Problems:</b><br/>
53977  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53978  * element will correct this<br/>
53979  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53980  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53981  * are unpredictable.<br/>
53982  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53983  * grid to calculate dimensions/offsets.<br/>
53984   * @constructor
53985  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53986  * The container MUST have some type of size defined for the grid to fill. The container will be
53987  * automatically set to position relative if it isn't already.
53988  * @param {Object} config A config object that sets properties on this grid.
53989  */
53990 Roo.grid.Grid = function(container, config){
53991         // initialize the container
53992         this.container = Roo.get(container);
53993         this.container.update("");
53994         this.container.setStyle("overflow", "hidden");
53995     this.container.addClass('x-grid-container');
53996
53997     this.id = this.container.id;
53998
53999     Roo.apply(this, config);
54000     // check and correct shorthanded configs
54001     if(this.ds){
54002         this.dataSource = this.ds;
54003         delete this.ds;
54004     }
54005     if(this.cm){
54006         this.colModel = this.cm;
54007         delete this.cm;
54008     }
54009     if(this.sm){
54010         this.selModel = this.sm;
54011         delete this.sm;
54012     }
54013
54014     if (this.selModel) {
54015         this.selModel = Roo.factory(this.selModel, Roo.grid);
54016         this.sm = this.selModel;
54017         this.sm.xmodule = this.xmodule || false;
54018     }
54019     if (typeof(this.colModel.config) == 'undefined') {
54020         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54021         this.cm = this.colModel;
54022         this.cm.xmodule = this.xmodule || false;
54023     }
54024     if (this.dataSource) {
54025         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54026         this.ds = this.dataSource;
54027         this.ds.xmodule = this.xmodule || false;
54028          
54029     }
54030     
54031     
54032     
54033     if(this.width){
54034         this.container.setWidth(this.width);
54035     }
54036
54037     if(this.height){
54038         this.container.setHeight(this.height);
54039     }
54040     /** @private */
54041         this.addEvents({
54042         // raw events
54043         /**
54044          * @event click
54045          * The raw click event for the entire grid.
54046          * @param {Roo.EventObject} e
54047          */
54048         "click" : true,
54049         /**
54050          * @event dblclick
54051          * The raw dblclick event for the entire grid.
54052          * @param {Roo.EventObject} e
54053          */
54054         "dblclick" : true,
54055         /**
54056          * @event contextmenu
54057          * The raw contextmenu event for the entire grid.
54058          * @param {Roo.EventObject} e
54059          */
54060         "contextmenu" : true,
54061         /**
54062          * @event mousedown
54063          * The raw mousedown event for the entire grid.
54064          * @param {Roo.EventObject} e
54065          */
54066         "mousedown" : true,
54067         /**
54068          * @event mouseup
54069          * The raw mouseup event for the entire grid.
54070          * @param {Roo.EventObject} e
54071          */
54072         "mouseup" : true,
54073         /**
54074          * @event mouseover
54075          * The raw mouseover event for the entire grid.
54076          * @param {Roo.EventObject} e
54077          */
54078         "mouseover" : true,
54079         /**
54080          * @event mouseout
54081          * The raw mouseout event for the entire grid.
54082          * @param {Roo.EventObject} e
54083          */
54084         "mouseout" : true,
54085         /**
54086          * @event keypress
54087          * The raw keypress event for the entire grid.
54088          * @param {Roo.EventObject} e
54089          */
54090         "keypress" : true,
54091         /**
54092          * @event keydown
54093          * The raw keydown event for the entire grid.
54094          * @param {Roo.EventObject} e
54095          */
54096         "keydown" : true,
54097
54098         // custom events
54099
54100         /**
54101          * @event cellclick
54102          * Fires when a cell is clicked
54103          * @param {Grid} this
54104          * @param {Number} rowIndex
54105          * @param {Number} columnIndex
54106          * @param {Roo.EventObject} e
54107          */
54108         "cellclick" : true,
54109         /**
54110          * @event celldblclick
54111          * Fires when a cell is double clicked
54112          * @param {Grid} this
54113          * @param {Number} rowIndex
54114          * @param {Number} columnIndex
54115          * @param {Roo.EventObject} e
54116          */
54117         "celldblclick" : true,
54118         /**
54119          * @event rowclick
54120          * Fires when a row is clicked
54121          * @param {Grid} this
54122          * @param {Number} rowIndex
54123          * @param {Roo.EventObject} e
54124          */
54125         "rowclick" : true,
54126         /**
54127          * @event rowdblclick
54128          * Fires when a row is double clicked
54129          * @param {Grid} this
54130          * @param {Number} rowIndex
54131          * @param {Roo.EventObject} e
54132          */
54133         "rowdblclick" : true,
54134         /**
54135          * @event headerclick
54136          * Fires when a header is clicked
54137          * @param {Grid} this
54138          * @param {Number} columnIndex
54139          * @param {Roo.EventObject} e
54140          */
54141         "headerclick" : true,
54142         /**
54143          * @event headerdblclick
54144          * Fires when a header cell is double clicked
54145          * @param {Grid} this
54146          * @param {Number} columnIndex
54147          * @param {Roo.EventObject} e
54148          */
54149         "headerdblclick" : true,
54150         /**
54151          * @event rowcontextmenu
54152          * Fires when a row is right clicked
54153          * @param {Grid} this
54154          * @param {Number} rowIndex
54155          * @param {Roo.EventObject} e
54156          */
54157         "rowcontextmenu" : true,
54158         /**
54159          * @event cellcontextmenu
54160          * Fires when a cell is right clicked
54161          * @param {Grid} this
54162          * @param {Number} rowIndex
54163          * @param {Number} cellIndex
54164          * @param {Roo.EventObject} e
54165          */
54166          "cellcontextmenu" : true,
54167         /**
54168          * @event headercontextmenu
54169          * Fires when a header is right clicked
54170          * @param {Grid} this
54171          * @param {Number} columnIndex
54172          * @param {Roo.EventObject} e
54173          */
54174         "headercontextmenu" : true,
54175         /**
54176          * @event bodyscroll
54177          * Fires when the body element is scrolled
54178          * @param {Number} scrollLeft
54179          * @param {Number} scrollTop
54180          */
54181         "bodyscroll" : true,
54182         /**
54183          * @event columnresize
54184          * Fires when the user resizes a column
54185          * @param {Number} columnIndex
54186          * @param {Number} newSize
54187          */
54188         "columnresize" : true,
54189         /**
54190          * @event columnmove
54191          * Fires when the user moves a column
54192          * @param {Number} oldIndex
54193          * @param {Number} newIndex
54194          */
54195         "columnmove" : true,
54196         /**
54197          * @event startdrag
54198          * Fires when row(s) start being dragged
54199          * @param {Grid} this
54200          * @param {Roo.GridDD} dd The drag drop object
54201          * @param {event} e The raw browser event
54202          */
54203         "startdrag" : true,
54204         /**
54205          * @event enddrag
54206          * Fires when a drag operation is complete
54207          * @param {Grid} this
54208          * @param {Roo.GridDD} dd The drag drop object
54209          * @param {event} e The raw browser event
54210          */
54211         "enddrag" : true,
54212         /**
54213          * @event dragdrop
54214          * Fires when dragged row(s) are dropped on a valid DD target
54215          * @param {Grid} this
54216          * @param {Roo.GridDD} dd The drag drop object
54217          * @param {String} targetId The target drag drop object
54218          * @param {event} e The raw browser event
54219          */
54220         "dragdrop" : true,
54221         /**
54222          * @event dragover
54223          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54224          * @param {Grid} this
54225          * @param {Roo.GridDD} dd The drag drop object
54226          * @param {String} targetId The target drag drop object
54227          * @param {event} e The raw browser event
54228          */
54229         "dragover" : true,
54230         /**
54231          * @event dragenter
54232          *  Fires when the dragged row(s) first cross another DD target while being dragged
54233          * @param {Grid} this
54234          * @param {Roo.GridDD} dd The drag drop object
54235          * @param {String} targetId The target drag drop object
54236          * @param {event} e The raw browser event
54237          */
54238         "dragenter" : true,
54239         /**
54240          * @event dragout
54241          * Fires when the dragged row(s) leave another DD target while being dragged
54242          * @param {Grid} this
54243          * @param {Roo.GridDD} dd The drag drop object
54244          * @param {String} targetId The target drag drop object
54245          * @param {event} e The raw browser event
54246          */
54247         "dragout" : true,
54248         /**
54249          * @event rowclass
54250          * Fires when a row is rendered, so you can change add a style to it.
54251          * @param {GridView} gridview   The grid view
54252          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54253          */
54254         'rowclass' : true,
54255
54256         /**
54257          * @event render
54258          * Fires when the grid is rendered
54259          * @param {Grid} grid
54260          */
54261         'render' : true
54262     });
54263
54264     Roo.grid.Grid.superclass.constructor.call(this);
54265 };
54266 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54267     
54268     /**
54269      * @cfg {String} ddGroup - drag drop group.
54270      */
54271
54272     /**
54273      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54274      */
54275     minColumnWidth : 25,
54276
54277     /**
54278      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54279      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54280      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54281      */
54282     autoSizeColumns : false,
54283
54284     /**
54285      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54286      */
54287     autoSizeHeaders : true,
54288
54289     /**
54290      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54291      */
54292     monitorWindowResize : true,
54293
54294     /**
54295      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54296      * rows measured to get a columns size. Default is 0 (all rows).
54297      */
54298     maxRowsToMeasure : 0,
54299
54300     /**
54301      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54302      */
54303     trackMouseOver : true,
54304
54305     /**
54306     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54307     */
54308     
54309     /**
54310     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54311     */
54312     enableDragDrop : false,
54313     
54314     /**
54315     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54316     */
54317     enableColumnMove : true,
54318     
54319     /**
54320     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54321     */
54322     enableColumnHide : true,
54323     
54324     /**
54325     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54326     */
54327     enableRowHeightSync : false,
54328     
54329     /**
54330     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54331     */
54332     stripeRows : true,
54333     
54334     /**
54335     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54336     */
54337     autoHeight : false,
54338
54339     /**
54340      * @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.
54341      */
54342     autoExpandColumn : false,
54343
54344     /**
54345     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54346     * Default is 50.
54347     */
54348     autoExpandMin : 50,
54349
54350     /**
54351     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54352     */
54353     autoExpandMax : 1000,
54354
54355     /**
54356     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54357     */
54358     view : null,
54359
54360     /**
54361     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54362     */
54363     loadMask : false,
54364     /**
54365     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54366     */
54367     dropTarget: false,
54368     
54369    
54370     
54371     // private
54372     rendered : false,
54373
54374     /**
54375     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54376     * of a fixed width. Default is false.
54377     */
54378     /**
54379     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54380     */
54381     /**
54382      * Called once after all setup has been completed and the grid is ready to be rendered.
54383      * @return {Roo.grid.Grid} this
54384      */
54385     render : function()
54386     {
54387         var c = this.container;
54388         // try to detect autoHeight/width mode
54389         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54390             this.autoHeight = true;
54391         }
54392         var view = this.getView();
54393         view.init(this);
54394
54395         c.on("click", this.onClick, this);
54396         c.on("dblclick", this.onDblClick, this);
54397         c.on("contextmenu", this.onContextMenu, this);
54398         c.on("keydown", this.onKeyDown, this);
54399         if (Roo.isTouch) {
54400             c.on("touchstart", this.onTouchStart, this);
54401         }
54402
54403         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54404
54405         this.getSelectionModel().init(this);
54406
54407         view.render();
54408
54409         if(this.loadMask){
54410             this.loadMask = new Roo.LoadMask(this.container,
54411                     Roo.apply({store:this.dataSource}, this.loadMask));
54412         }
54413         
54414         
54415         if (this.toolbar && this.toolbar.xtype) {
54416             this.toolbar.container = this.getView().getHeaderPanel(true);
54417             this.toolbar = new Roo.Toolbar(this.toolbar);
54418         }
54419         if (this.footer && this.footer.xtype) {
54420             this.footer.dataSource = this.getDataSource();
54421             this.footer.container = this.getView().getFooterPanel(true);
54422             this.footer = Roo.factory(this.footer, Roo);
54423         }
54424         if (this.dropTarget && this.dropTarget.xtype) {
54425             delete this.dropTarget.xtype;
54426             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54427         }
54428         
54429         
54430         this.rendered = true;
54431         this.fireEvent('render', this);
54432         return this;
54433     },
54434
54435         /**
54436          * Reconfigures the grid to use a different Store and Column Model.
54437          * The View will be bound to the new objects and refreshed.
54438          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54439          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54440          */
54441     reconfigure : function(dataSource, colModel){
54442         if(this.loadMask){
54443             this.loadMask.destroy();
54444             this.loadMask = new Roo.LoadMask(this.container,
54445                     Roo.apply({store:dataSource}, this.loadMask));
54446         }
54447         this.view.bind(dataSource, colModel);
54448         this.dataSource = dataSource;
54449         this.colModel = colModel;
54450         this.view.refresh(true);
54451     },
54452
54453     // private
54454     onKeyDown : function(e){
54455         this.fireEvent("keydown", e);
54456     },
54457
54458     /**
54459      * Destroy this grid.
54460      * @param {Boolean} removeEl True to remove the element
54461      */
54462     destroy : function(removeEl, keepListeners){
54463         if(this.loadMask){
54464             this.loadMask.destroy();
54465         }
54466         var c = this.container;
54467         c.removeAllListeners();
54468         this.view.destroy();
54469         this.colModel.purgeListeners();
54470         if(!keepListeners){
54471             this.purgeListeners();
54472         }
54473         c.update("");
54474         if(removeEl === true){
54475             c.remove();
54476         }
54477     },
54478
54479     // private
54480     processEvent : function(name, e){
54481         // does this fire select???
54482         //Roo.log('grid:processEvent '  + name);
54483         
54484         if (name != 'touchstart' ) {
54485             this.fireEvent(name, e);    
54486         }
54487         
54488         var t = e.getTarget();
54489         var v = this.view;
54490         var header = v.findHeaderIndex(t);
54491         if(header !== false){
54492             var ename = name == 'touchstart' ? 'click' : name;
54493              
54494             this.fireEvent("header" + ename, this, header, e);
54495         }else{
54496             var row = v.findRowIndex(t);
54497             var cell = v.findCellIndex(t);
54498             if (name == 'touchstart') {
54499                 // first touch is always a click.
54500                 // hopefull this happens after selection is updated.?
54501                 name = false;
54502                 
54503                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54504                     var cs = this.selModel.getSelectedCell();
54505                     if (row == cs[0] && cell == cs[1]){
54506                         name = 'dblclick';
54507                     }
54508                 }
54509                 if (typeof(this.selModel.getSelections) != 'undefined') {
54510                     var cs = this.selModel.getSelections();
54511                     var ds = this.dataSource;
54512                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54513                         name = 'dblclick';
54514                     }
54515                 }
54516                 if (!name) {
54517                     return;
54518                 }
54519             }
54520             
54521             
54522             if(row !== false){
54523                 this.fireEvent("row" + name, this, row, e);
54524                 if(cell !== false){
54525                     this.fireEvent("cell" + name, this, row, cell, e);
54526                 }
54527             }
54528         }
54529     },
54530
54531     // private
54532     onClick : function(e){
54533         this.processEvent("click", e);
54534     },
54535    // private
54536     onTouchStart : function(e){
54537         this.processEvent("touchstart", e);
54538     },
54539
54540     // private
54541     onContextMenu : function(e, t){
54542         this.processEvent("contextmenu", e);
54543     },
54544
54545     // private
54546     onDblClick : function(e){
54547         this.processEvent("dblclick", e);
54548     },
54549
54550     // private
54551     walkCells : function(row, col, step, fn, scope){
54552         var cm = this.colModel, clen = cm.getColumnCount();
54553         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54554         if(step < 0){
54555             if(col < 0){
54556                 row--;
54557                 first = false;
54558             }
54559             while(row >= 0){
54560                 if(!first){
54561                     col = clen-1;
54562                 }
54563                 first = false;
54564                 while(col >= 0){
54565                     if(fn.call(scope || this, row, col, cm) === true){
54566                         return [row, col];
54567                     }
54568                     col--;
54569                 }
54570                 row--;
54571             }
54572         } else {
54573             if(col >= clen){
54574                 row++;
54575                 first = false;
54576             }
54577             while(row < rlen){
54578                 if(!first){
54579                     col = 0;
54580                 }
54581                 first = false;
54582                 while(col < clen){
54583                     if(fn.call(scope || this, row, col, cm) === true){
54584                         return [row, col];
54585                     }
54586                     col++;
54587                 }
54588                 row++;
54589             }
54590         }
54591         return null;
54592     },
54593
54594     // private
54595     getSelections : function(){
54596         return this.selModel.getSelections();
54597     },
54598
54599     /**
54600      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54601      * but if manual update is required this method will initiate it.
54602      */
54603     autoSize : function(){
54604         if(this.rendered){
54605             this.view.layout();
54606             if(this.view.adjustForScroll){
54607                 this.view.adjustForScroll();
54608             }
54609         }
54610     },
54611
54612     /**
54613      * Returns the grid's underlying element.
54614      * @return {Element} The element
54615      */
54616     getGridEl : function(){
54617         return this.container;
54618     },
54619
54620     // private for compatibility, overridden by editor grid
54621     stopEditing : function(){},
54622
54623     /**
54624      * Returns the grid's SelectionModel.
54625      * @return {SelectionModel}
54626      */
54627     getSelectionModel : function(){
54628         if(!this.selModel){
54629             this.selModel = new Roo.grid.RowSelectionModel();
54630         }
54631         return this.selModel;
54632     },
54633
54634     /**
54635      * Returns the grid's DataSource.
54636      * @return {DataSource}
54637      */
54638     getDataSource : function(){
54639         return this.dataSource;
54640     },
54641
54642     /**
54643      * Returns the grid's ColumnModel.
54644      * @return {ColumnModel}
54645      */
54646     getColumnModel : function(){
54647         return this.colModel;
54648     },
54649
54650     /**
54651      * Returns the grid's GridView object.
54652      * @return {GridView}
54653      */
54654     getView : function(){
54655         if(!this.view){
54656             this.view = new Roo.grid.GridView(this.viewConfig);
54657         }
54658         return this.view;
54659     },
54660     /**
54661      * Called to get grid's drag proxy text, by default returns this.ddText.
54662      * @return {String}
54663      */
54664     getDragDropText : function(){
54665         var count = this.selModel.getCount();
54666         return String.format(this.ddText, count, count == 1 ? '' : 's');
54667     }
54668 });
54669 /**
54670  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54671  * %0 is replaced with the number of selected rows.
54672  * @type String
54673  */
54674 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54675  * Based on:
54676  * Ext JS Library 1.1.1
54677  * Copyright(c) 2006-2007, Ext JS, LLC.
54678  *
54679  * Originally Released Under LGPL - original licence link has changed is not relivant.
54680  *
54681  * Fork - LGPL
54682  * <script type="text/javascript">
54683  */
54684  
54685 Roo.grid.AbstractGridView = function(){
54686         this.grid = null;
54687         
54688         this.events = {
54689             "beforerowremoved" : true,
54690             "beforerowsinserted" : true,
54691             "beforerefresh" : true,
54692             "rowremoved" : true,
54693             "rowsinserted" : true,
54694             "rowupdated" : true,
54695             "refresh" : true
54696         };
54697     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54698 };
54699
54700 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54701     rowClass : "x-grid-row",
54702     cellClass : "x-grid-cell",
54703     tdClass : "x-grid-td",
54704     hdClass : "x-grid-hd",
54705     splitClass : "x-grid-hd-split",
54706     
54707     init: function(grid){
54708         this.grid = grid;
54709                 var cid = this.grid.getGridEl().id;
54710         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54711         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54712         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54713         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54714         },
54715         
54716     getColumnRenderers : function(){
54717         var renderers = [];
54718         var cm = this.grid.colModel;
54719         var colCount = cm.getColumnCount();
54720         for(var i = 0; i < colCount; i++){
54721             renderers[i] = cm.getRenderer(i);
54722         }
54723         return renderers;
54724     },
54725     
54726     getColumnIds : function(){
54727         var ids = [];
54728         var cm = this.grid.colModel;
54729         var colCount = cm.getColumnCount();
54730         for(var i = 0; i < colCount; i++){
54731             ids[i] = cm.getColumnId(i);
54732         }
54733         return ids;
54734     },
54735     
54736     getDataIndexes : function(){
54737         if(!this.indexMap){
54738             this.indexMap = this.buildIndexMap();
54739         }
54740         return this.indexMap.colToData;
54741     },
54742     
54743     getColumnIndexByDataIndex : function(dataIndex){
54744         if(!this.indexMap){
54745             this.indexMap = this.buildIndexMap();
54746         }
54747         return this.indexMap.dataToCol[dataIndex];
54748     },
54749     
54750     /**
54751      * Set a css style for a column dynamically. 
54752      * @param {Number} colIndex The index of the column
54753      * @param {String} name The css property name
54754      * @param {String} value The css value
54755      */
54756     setCSSStyle : function(colIndex, name, value){
54757         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54758         Roo.util.CSS.updateRule(selector, name, value);
54759     },
54760     
54761     generateRules : function(cm){
54762         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54763         Roo.util.CSS.removeStyleSheet(rulesId);
54764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54765             var cid = cm.getColumnId(i);
54766             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54767                          this.tdSelector, cid, " {\n}\n",
54768                          this.hdSelector, cid, " {\n}\n",
54769                          this.splitSelector, cid, " {\n}\n");
54770         }
54771         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54772     }
54773 });/*
54774  * Based on:
54775  * Ext JS Library 1.1.1
54776  * Copyright(c) 2006-2007, Ext JS, LLC.
54777  *
54778  * Originally Released Under LGPL - original licence link has changed is not relivant.
54779  *
54780  * Fork - LGPL
54781  * <script type="text/javascript">
54782  */
54783
54784 // private
54785 // This is a support class used internally by the Grid components
54786 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54787     this.grid = grid;
54788     this.view = grid.getView();
54789     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54790     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54791     if(hd2){
54792         this.setHandleElId(Roo.id(hd));
54793         this.setOuterHandleElId(Roo.id(hd2));
54794     }
54795     this.scroll = false;
54796 };
54797 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54798     maxDragWidth: 120,
54799     getDragData : function(e){
54800         var t = Roo.lib.Event.getTarget(e);
54801         var h = this.view.findHeaderCell(t);
54802         if(h){
54803             return {ddel: h.firstChild, header:h};
54804         }
54805         return false;
54806     },
54807
54808     onInitDrag : function(e){
54809         this.view.headersDisabled = true;
54810         var clone = this.dragData.ddel.cloneNode(true);
54811         clone.id = Roo.id();
54812         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54813         this.proxy.update(clone);
54814         return true;
54815     },
54816
54817     afterValidDrop : function(){
54818         var v = this.view;
54819         setTimeout(function(){
54820             v.headersDisabled = false;
54821         }, 50);
54822     },
54823
54824     afterInvalidDrop : function(){
54825         var v = this.view;
54826         setTimeout(function(){
54827             v.headersDisabled = false;
54828         }, 50);
54829     }
54830 });
54831 /*
54832  * Based on:
54833  * Ext JS Library 1.1.1
54834  * Copyright(c) 2006-2007, Ext JS, LLC.
54835  *
54836  * Originally Released Under LGPL - original licence link has changed is not relivant.
54837  *
54838  * Fork - LGPL
54839  * <script type="text/javascript">
54840  */
54841 // private
54842 // This is a support class used internally by the Grid components
54843 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54844     this.grid = grid;
54845     this.view = grid.getView();
54846     // split the proxies so they don't interfere with mouse events
54847     this.proxyTop = Roo.DomHelper.append(document.body, {
54848         cls:"col-move-top", html:"&#160;"
54849     }, true);
54850     this.proxyBottom = Roo.DomHelper.append(document.body, {
54851         cls:"col-move-bottom", html:"&#160;"
54852     }, true);
54853     this.proxyTop.hide = this.proxyBottom.hide = function(){
54854         this.setLeftTop(-100,-100);
54855         this.setStyle("visibility", "hidden");
54856     };
54857     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54858     // temporarily disabled
54859     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54860     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54861 };
54862 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54863     proxyOffsets : [-4, -9],
54864     fly: Roo.Element.fly,
54865
54866     getTargetFromEvent : function(e){
54867         var t = Roo.lib.Event.getTarget(e);
54868         var cindex = this.view.findCellIndex(t);
54869         if(cindex !== false){
54870             return this.view.getHeaderCell(cindex);
54871         }
54872         return null;
54873     },
54874
54875     nextVisible : function(h){
54876         var v = this.view, cm = this.grid.colModel;
54877         h = h.nextSibling;
54878         while(h){
54879             if(!cm.isHidden(v.getCellIndex(h))){
54880                 return h;
54881             }
54882             h = h.nextSibling;
54883         }
54884         return null;
54885     },
54886
54887     prevVisible : function(h){
54888         var v = this.view, cm = this.grid.colModel;
54889         h = h.prevSibling;
54890         while(h){
54891             if(!cm.isHidden(v.getCellIndex(h))){
54892                 return h;
54893             }
54894             h = h.prevSibling;
54895         }
54896         return null;
54897     },
54898
54899     positionIndicator : function(h, n, e){
54900         var x = Roo.lib.Event.getPageX(e);
54901         var r = Roo.lib.Dom.getRegion(n.firstChild);
54902         var px, pt, py = r.top + this.proxyOffsets[1];
54903         if((r.right - x) <= (r.right-r.left)/2){
54904             px = r.right+this.view.borderWidth;
54905             pt = "after";
54906         }else{
54907             px = r.left;
54908             pt = "before";
54909         }
54910         var oldIndex = this.view.getCellIndex(h);
54911         var newIndex = this.view.getCellIndex(n);
54912
54913         if(this.grid.colModel.isFixed(newIndex)){
54914             return false;
54915         }
54916
54917         var locked = this.grid.colModel.isLocked(newIndex);
54918
54919         if(pt == "after"){
54920             newIndex++;
54921         }
54922         if(oldIndex < newIndex){
54923             newIndex--;
54924         }
54925         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54926             return false;
54927         }
54928         px +=  this.proxyOffsets[0];
54929         this.proxyTop.setLeftTop(px, py);
54930         this.proxyTop.show();
54931         if(!this.bottomOffset){
54932             this.bottomOffset = this.view.mainHd.getHeight();
54933         }
54934         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54935         this.proxyBottom.show();
54936         return pt;
54937     },
54938
54939     onNodeEnter : function(n, dd, e, data){
54940         if(data.header != n){
54941             this.positionIndicator(data.header, n, e);
54942         }
54943     },
54944
54945     onNodeOver : function(n, dd, e, data){
54946         var result = false;
54947         if(data.header != n){
54948             result = this.positionIndicator(data.header, n, e);
54949         }
54950         if(!result){
54951             this.proxyTop.hide();
54952             this.proxyBottom.hide();
54953         }
54954         return result ? this.dropAllowed : this.dropNotAllowed;
54955     },
54956
54957     onNodeOut : function(n, dd, e, data){
54958         this.proxyTop.hide();
54959         this.proxyBottom.hide();
54960     },
54961
54962     onNodeDrop : function(n, dd, e, data){
54963         var h = data.header;
54964         if(h != n){
54965             var cm = this.grid.colModel;
54966             var x = Roo.lib.Event.getPageX(e);
54967             var r = Roo.lib.Dom.getRegion(n.firstChild);
54968             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54969             var oldIndex = this.view.getCellIndex(h);
54970             var newIndex = this.view.getCellIndex(n);
54971             var locked = cm.isLocked(newIndex);
54972             if(pt == "after"){
54973                 newIndex++;
54974             }
54975             if(oldIndex < newIndex){
54976                 newIndex--;
54977             }
54978             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54979                 return false;
54980             }
54981             cm.setLocked(oldIndex, locked, true);
54982             cm.moveColumn(oldIndex, newIndex);
54983             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54984             return true;
54985         }
54986         return false;
54987     }
54988 });
54989 /*
54990  * Based on:
54991  * Ext JS Library 1.1.1
54992  * Copyright(c) 2006-2007, Ext JS, LLC.
54993  *
54994  * Originally Released Under LGPL - original licence link has changed is not relivant.
54995  *
54996  * Fork - LGPL
54997  * <script type="text/javascript">
54998  */
54999   
55000 /**
55001  * @class Roo.grid.GridView
55002  * @extends Roo.util.Observable
55003  *
55004  * @constructor
55005  * @param {Object} config
55006  */
55007 Roo.grid.GridView = function(config){
55008     Roo.grid.GridView.superclass.constructor.call(this);
55009     this.el = null;
55010
55011     Roo.apply(this, config);
55012 };
55013
55014 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55015
55016     unselectable :  'unselectable="on"',
55017     unselectableCls :  'x-unselectable',
55018     
55019     
55020     rowClass : "x-grid-row",
55021
55022     cellClass : "x-grid-col",
55023
55024     tdClass : "x-grid-td",
55025
55026     hdClass : "x-grid-hd",
55027
55028     splitClass : "x-grid-split",
55029
55030     sortClasses : ["sort-asc", "sort-desc"],
55031
55032     enableMoveAnim : false,
55033
55034     hlColor: "C3DAF9",
55035
55036     dh : Roo.DomHelper,
55037
55038     fly : Roo.Element.fly,
55039
55040     css : Roo.util.CSS,
55041
55042     borderWidth: 1,
55043
55044     splitOffset: 3,
55045
55046     scrollIncrement : 22,
55047
55048     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55049
55050     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55051
55052     bind : function(ds, cm){
55053         if(this.ds){
55054             this.ds.un("load", this.onLoad, this);
55055             this.ds.un("datachanged", this.onDataChange, this);
55056             this.ds.un("add", this.onAdd, this);
55057             this.ds.un("remove", this.onRemove, this);
55058             this.ds.un("update", this.onUpdate, this);
55059             this.ds.un("clear", this.onClear, this);
55060         }
55061         if(ds){
55062             ds.on("load", this.onLoad, this);
55063             ds.on("datachanged", this.onDataChange, this);
55064             ds.on("add", this.onAdd, this);
55065             ds.on("remove", this.onRemove, this);
55066             ds.on("update", this.onUpdate, this);
55067             ds.on("clear", this.onClear, this);
55068         }
55069         this.ds = ds;
55070
55071         if(this.cm){
55072             this.cm.un("widthchange", this.onColWidthChange, this);
55073             this.cm.un("headerchange", this.onHeaderChange, this);
55074             this.cm.un("hiddenchange", this.onHiddenChange, this);
55075             this.cm.un("columnmoved", this.onColumnMove, this);
55076             this.cm.un("columnlockchange", this.onColumnLock, this);
55077         }
55078         if(cm){
55079             this.generateRules(cm);
55080             cm.on("widthchange", this.onColWidthChange, this);
55081             cm.on("headerchange", this.onHeaderChange, this);
55082             cm.on("hiddenchange", this.onHiddenChange, this);
55083             cm.on("columnmoved", this.onColumnMove, this);
55084             cm.on("columnlockchange", this.onColumnLock, this);
55085         }
55086         this.cm = cm;
55087     },
55088
55089     init: function(grid){
55090         Roo.grid.GridView.superclass.init.call(this, grid);
55091
55092         this.bind(grid.dataSource, grid.colModel);
55093
55094         grid.on("headerclick", this.handleHeaderClick, this);
55095
55096         if(grid.trackMouseOver){
55097             grid.on("mouseover", this.onRowOver, this);
55098             grid.on("mouseout", this.onRowOut, this);
55099         }
55100         grid.cancelTextSelection = function(){};
55101         this.gridId = grid.id;
55102
55103         var tpls = this.templates || {};
55104
55105         if(!tpls.master){
55106             tpls.master = new Roo.Template(
55107                '<div class="x-grid" hidefocus="true">',
55108                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55109                   '<div class="x-grid-topbar"></div>',
55110                   '<div class="x-grid-scroller"><div></div></div>',
55111                   '<div class="x-grid-locked">',
55112                       '<div class="x-grid-header">{lockedHeader}</div>',
55113                       '<div class="x-grid-body">{lockedBody}</div>',
55114                   "</div>",
55115                   '<div class="x-grid-viewport">',
55116                       '<div class="x-grid-header">{header}</div>',
55117                       '<div class="x-grid-body">{body}</div>',
55118                   "</div>",
55119                   '<div class="x-grid-bottombar"></div>',
55120                  
55121                   '<div class="x-grid-resize-proxy">&#160;</div>',
55122                "</div>"
55123             );
55124             tpls.master.disableformats = true;
55125         }
55126
55127         if(!tpls.header){
55128             tpls.header = new Roo.Template(
55129                '<table border="0" cellspacing="0" cellpadding="0">',
55130                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55131                "</table>{splits}"
55132             );
55133             tpls.header.disableformats = true;
55134         }
55135         tpls.header.compile();
55136
55137         if(!tpls.hcell){
55138             tpls.hcell = new Roo.Template(
55139                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55140                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55141                 "</div></td>"
55142              );
55143              tpls.hcell.disableFormats = true;
55144         }
55145         tpls.hcell.compile();
55146
55147         if(!tpls.hsplit){
55148             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55149                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55150             tpls.hsplit.disableFormats = true;
55151         }
55152         tpls.hsplit.compile();
55153
55154         if(!tpls.body){
55155             tpls.body = new Roo.Template(
55156                '<table border="0" cellspacing="0" cellpadding="0">',
55157                "<tbody>{rows}</tbody>",
55158                "</table>"
55159             );
55160             tpls.body.disableFormats = true;
55161         }
55162         tpls.body.compile();
55163
55164         if(!tpls.row){
55165             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55166             tpls.row.disableFormats = true;
55167         }
55168         tpls.row.compile();
55169
55170         if(!tpls.cell){
55171             tpls.cell = new Roo.Template(
55172                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55173                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55174                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55175                 "</td>"
55176             );
55177             tpls.cell.disableFormats = true;
55178         }
55179         tpls.cell.compile();
55180
55181         this.templates = tpls;
55182     },
55183
55184     // remap these for backwards compat
55185     onColWidthChange : function(){
55186         this.updateColumns.apply(this, arguments);
55187     },
55188     onHeaderChange : function(){
55189         this.updateHeaders.apply(this, arguments);
55190     }, 
55191     onHiddenChange : function(){
55192         this.handleHiddenChange.apply(this, arguments);
55193     },
55194     onColumnMove : function(){
55195         this.handleColumnMove.apply(this, arguments);
55196     },
55197     onColumnLock : function(){
55198         this.handleLockChange.apply(this, arguments);
55199     },
55200
55201     onDataChange : function(){
55202         this.refresh();
55203         this.updateHeaderSortState();
55204     },
55205
55206     onClear : function(){
55207         this.refresh();
55208     },
55209
55210     onUpdate : function(ds, record){
55211         this.refreshRow(record);
55212     },
55213
55214     refreshRow : function(record){
55215         var ds = this.ds, index;
55216         if(typeof record == 'number'){
55217             index = record;
55218             record = ds.getAt(index);
55219         }else{
55220             index = ds.indexOf(record);
55221         }
55222         this.insertRows(ds, index, index, true);
55223         this.onRemove(ds, record, index+1, true);
55224         this.syncRowHeights(index, index);
55225         this.layout();
55226         this.fireEvent("rowupdated", this, index, record);
55227     },
55228
55229     onAdd : function(ds, records, index){
55230         this.insertRows(ds, index, index + (records.length-1));
55231     },
55232
55233     onRemove : function(ds, record, index, isUpdate){
55234         if(isUpdate !== true){
55235             this.fireEvent("beforerowremoved", this, index, record);
55236         }
55237         var bt = this.getBodyTable(), lt = this.getLockedTable();
55238         if(bt.rows[index]){
55239             bt.firstChild.removeChild(bt.rows[index]);
55240         }
55241         if(lt.rows[index]){
55242             lt.firstChild.removeChild(lt.rows[index]);
55243         }
55244         if(isUpdate !== true){
55245             this.stripeRows(index);
55246             this.syncRowHeights(index, index);
55247             this.layout();
55248             this.fireEvent("rowremoved", this, index, record);
55249         }
55250     },
55251
55252     onLoad : function(){
55253         this.scrollToTop();
55254     },
55255
55256     /**
55257      * Scrolls the grid to the top
55258      */
55259     scrollToTop : function(){
55260         if(this.scroller){
55261             this.scroller.dom.scrollTop = 0;
55262             this.syncScroll();
55263         }
55264     },
55265
55266     /**
55267      * Gets a panel in the header of the grid that can be used for toolbars etc.
55268      * After modifying the contents of this panel a call to grid.autoSize() may be
55269      * required to register any changes in size.
55270      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55271      * @return Roo.Element
55272      */
55273     getHeaderPanel : function(doShow){
55274         if(doShow){
55275             this.headerPanel.show();
55276         }
55277         return this.headerPanel;
55278     },
55279
55280     /**
55281      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55282      * After modifying the contents of this panel a call to grid.autoSize() may be
55283      * required to register any changes in size.
55284      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55285      * @return Roo.Element
55286      */
55287     getFooterPanel : function(doShow){
55288         if(doShow){
55289             this.footerPanel.show();
55290         }
55291         return this.footerPanel;
55292     },
55293
55294     initElements : function(){
55295         var E = Roo.Element;
55296         var el = this.grid.getGridEl().dom.firstChild;
55297         var cs = el.childNodes;
55298
55299         this.el = new E(el);
55300         
55301          this.focusEl = new E(el.firstChild);
55302         this.focusEl.swallowEvent("click", true);
55303         
55304         this.headerPanel = new E(cs[1]);
55305         this.headerPanel.enableDisplayMode("block");
55306
55307         this.scroller = new E(cs[2]);
55308         this.scrollSizer = new E(this.scroller.dom.firstChild);
55309
55310         this.lockedWrap = new E(cs[3]);
55311         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55312         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55313
55314         this.mainWrap = new E(cs[4]);
55315         this.mainHd = new E(this.mainWrap.dom.firstChild);
55316         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55317
55318         this.footerPanel = new E(cs[5]);
55319         this.footerPanel.enableDisplayMode("block");
55320
55321         this.resizeProxy = new E(cs[6]);
55322
55323         this.headerSelector = String.format(
55324            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55325            this.lockedHd.id, this.mainHd.id
55326         );
55327
55328         this.splitterSelector = String.format(
55329            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55330            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55331         );
55332     },
55333     idToCssName : function(s)
55334     {
55335         return s.replace(/[^a-z0-9]+/ig, '-');
55336     },
55337
55338     getHeaderCell : function(index){
55339         return Roo.DomQuery.select(this.headerSelector)[index];
55340     },
55341
55342     getHeaderCellMeasure : function(index){
55343         return this.getHeaderCell(index).firstChild;
55344     },
55345
55346     getHeaderCellText : function(index){
55347         return this.getHeaderCell(index).firstChild.firstChild;
55348     },
55349
55350     getLockedTable : function(){
55351         return this.lockedBody.dom.firstChild;
55352     },
55353
55354     getBodyTable : function(){
55355         return this.mainBody.dom.firstChild;
55356     },
55357
55358     getLockedRow : function(index){
55359         return this.getLockedTable().rows[index];
55360     },
55361
55362     getRow : function(index){
55363         return this.getBodyTable().rows[index];
55364     },
55365
55366     getRowComposite : function(index){
55367         if(!this.rowEl){
55368             this.rowEl = new Roo.CompositeElementLite();
55369         }
55370         var els = [], lrow, mrow;
55371         if(lrow = this.getLockedRow(index)){
55372             els.push(lrow);
55373         }
55374         if(mrow = this.getRow(index)){
55375             els.push(mrow);
55376         }
55377         this.rowEl.elements = els;
55378         return this.rowEl;
55379     },
55380     /**
55381      * Gets the 'td' of the cell
55382      * 
55383      * @param {Integer} rowIndex row to select
55384      * @param {Integer} colIndex column to select
55385      * 
55386      * @return {Object} 
55387      */
55388     getCell : function(rowIndex, colIndex){
55389         var locked = this.cm.getLockedCount();
55390         var source;
55391         if(colIndex < locked){
55392             source = this.lockedBody.dom.firstChild;
55393         }else{
55394             source = this.mainBody.dom.firstChild;
55395             colIndex -= locked;
55396         }
55397         return source.rows[rowIndex].childNodes[colIndex];
55398     },
55399
55400     getCellText : function(rowIndex, colIndex){
55401         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55402     },
55403
55404     getCellBox : function(cell){
55405         var b = this.fly(cell).getBox();
55406         if(Roo.isOpera){ // opera fails to report the Y
55407             b.y = cell.offsetTop + this.mainBody.getY();
55408         }
55409         return b;
55410     },
55411
55412     getCellIndex : function(cell){
55413         var id = String(cell.className).match(this.cellRE);
55414         if(id){
55415             return parseInt(id[1], 10);
55416         }
55417         return 0;
55418     },
55419
55420     findHeaderIndex : function(n){
55421         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55422         return r ? this.getCellIndex(r) : false;
55423     },
55424
55425     findHeaderCell : function(n){
55426         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55427         return r ? r : false;
55428     },
55429
55430     findRowIndex : function(n){
55431         if(!n){
55432             return false;
55433         }
55434         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55435         return r ? r.rowIndex : false;
55436     },
55437
55438     findCellIndex : function(node){
55439         var stop = this.el.dom;
55440         while(node && node != stop){
55441             if(this.findRE.test(node.className)){
55442                 return this.getCellIndex(node);
55443             }
55444             node = node.parentNode;
55445         }
55446         return false;
55447     },
55448
55449     getColumnId : function(index){
55450         return this.cm.getColumnId(index);
55451     },
55452
55453     getSplitters : function()
55454     {
55455         if(this.splitterSelector){
55456            return Roo.DomQuery.select(this.splitterSelector);
55457         }else{
55458             return null;
55459       }
55460     },
55461
55462     getSplitter : function(index){
55463         return this.getSplitters()[index];
55464     },
55465
55466     onRowOver : function(e, t){
55467         var row;
55468         if((row = this.findRowIndex(t)) !== false){
55469             this.getRowComposite(row).addClass("x-grid-row-over");
55470         }
55471     },
55472
55473     onRowOut : function(e, t){
55474         var row;
55475         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55476             this.getRowComposite(row).removeClass("x-grid-row-over");
55477         }
55478     },
55479
55480     renderHeaders : function(){
55481         var cm = this.cm;
55482         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55483         var cb = [], lb = [], sb = [], lsb = [], p = {};
55484         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55485             p.cellId = "x-grid-hd-0-" + i;
55486             p.splitId = "x-grid-csplit-0-" + i;
55487             p.id = cm.getColumnId(i);
55488             p.value = cm.getColumnHeader(i) || "";
55489             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55490             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55491             if(!cm.isLocked(i)){
55492                 cb[cb.length] = ct.apply(p);
55493                 sb[sb.length] = st.apply(p);
55494             }else{
55495                 lb[lb.length] = ct.apply(p);
55496                 lsb[lsb.length] = st.apply(p);
55497             }
55498         }
55499         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55500                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55501     },
55502
55503     updateHeaders : function(){
55504         var html = this.renderHeaders();
55505         this.lockedHd.update(html[0]);
55506         this.mainHd.update(html[1]);
55507     },
55508
55509     /**
55510      * Focuses the specified row.
55511      * @param {Number} row The row index
55512      */
55513     focusRow : function(row)
55514     {
55515         //Roo.log('GridView.focusRow');
55516         var x = this.scroller.dom.scrollLeft;
55517         this.focusCell(row, 0, false);
55518         this.scroller.dom.scrollLeft = x;
55519     },
55520
55521     /**
55522      * Focuses the specified cell.
55523      * @param {Number} row The row index
55524      * @param {Number} col The column index
55525      * @param {Boolean} hscroll false to disable horizontal scrolling
55526      */
55527     focusCell : function(row, col, hscroll)
55528     {
55529         //Roo.log('GridView.focusCell');
55530         var el = this.ensureVisible(row, col, hscroll);
55531         this.focusEl.alignTo(el, "tl-tl");
55532         if(Roo.isGecko){
55533             this.focusEl.focus();
55534         }else{
55535             this.focusEl.focus.defer(1, this.focusEl);
55536         }
55537     },
55538
55539     /**
55540      * Scrolls the specified cell into view
55541      * @param {Number} row The row index
55542      * @param {Number} col The column index
55543      * @param {Boolean} hscroll false to disable horizontal scrolling
55544      */
55545     ensureVisible : function(row, col, hscroll)
55546     {
55547         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55548         //return null; //disable for testing.
55549         if(typeof row != "number"){
55550             row = row.rowIndex;
55551         }
55552         if(row < 0 && row >= this.ds.getCount()){
55553             return  null;
55554         }
55555         col = (col !== undefined ? col : 0);
55556         var cm = this.grid.colModel;
55557         while(cm.isHidden(col)){
55558             col++;
55559         }
55560
55561         var el = this.getCell(row, col);
55562         if(!el){
55563             return null;
55564         }
55565         var c = this.scroller.dom;
55566
55567         var ctop = parseInt(el.offsetTop, 10);
55568         var cleft = parseInt(el.offsetLeft, 10);
55569         var cbot = ctop + el.offsetHeight;
55570         var cright = cleft + el.offsetWidth;
55571         
55572         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55573         var stop = parseInt(c.scrollTop, 10);
55574         var sleft = parseInt(c.scrollLeft, 10);
55575         var sbot = stop + ch;
55576         var sright = sleft + c.clientWidth;
55577         /*
55578         Roo.log('GridView.ensureVisible:' +
55579                 ' ctop:' + ctop +
55580                 ' c.clientHeight:' + c.clientHeight +
55581                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55582                 ' stop:' + stop +
55583                 ' cbot:' + cbot +
55584                 ' sbot:' + sbot +
55585                 ' ch:' + ch  
55586                 );
55587         */
55588         if(ctop < stop){
55589              c.scrollTop = ctop;
55590             //Roo.log("set scrolltop to ctop DISABLE?");
55591         }else if(cbot > sbot){
55592             //Roo.log("set scrolltop to cbot-ch");
55593             c.scrollTop = cbot-ch;
55594         }
55595         
55596         if(hscroll !== false){
55597             if(cleft < sleft){
55598                 c.scrollLeft = cleft;
55599             }else if(cright > sright){
55600                 c.scrollLeft = cright-c.clientWidth;
55601             }
55602         }
55603          
55604         return el;
55605     },
55606
55607     updateColumns : function(){
55608         this.grid.stopEditing();
55609         var cm = this.grid.colModel, colIds = this.getColumnIds();
55610         //var totalWidth = cm.getTotalWidth();
55611         var pos = 0;
55612         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55613             //if(cm.isHidden(i)) continue;
55614             var w = cm.getColumnWidth(i);
55615             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55616             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55617         }
55618         this.updateSplitters();
55619     },
55620
55621     generateRules : function(cm){
55622         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55623         Roo.util.CSS.removeStyleSheet(rulesId);
55624         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55625             var cid = cm.getColumnId(i);
55626             var align = '';
55627             if(cm.config[i].align){
55628                 align = 'text-align:'+cm.config[i].align+';';
55629             }
55630             var hidden = '';
55631             if(cm.isHidden(i)){
55632                 hidden = 'display:none;';
55633             }
55634             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55635             ruleBuf.push(
55636                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55637                     this.hdSelector, cid, " {\n", align, width, "}\n",
55638                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55639                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55640         }
55641         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55642     },
55643
55644     updateSplitters : function(){
55645         var cm = this.cm, s = this.getSplitters();
55646         if(s){ // splitters not created yet
55647             var pos = 0, locked = true;
55648             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55649                 if(cm.isHidden(i)) {
55650                     continue;
55651                 }
55652                 var w = cm.getColumnWidth(i); // make sure it's a number
55653                 if(!cm.isLocked(i) && locked){
55654                     pos = 0;
55655                     locked = false;
55656                 }
55657                 pos += w;
55658                 s[i].style.left = (pos-this.splitOffset) + "px";
55659             }
55660         }
55661     },
55662
55663     handleHiddenChange : function(colModel, colIndex, hidden){
55664         if(hidden){
55665             this.hideColumn(colIndex);
55666         }else{
55667             this.unhideColumn(colIndex);
55668         }
55669     },
55670
55671     hideColumn : function(colIndex){
55672         var cid = this.getColumnId(colIndex);
55673         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55674         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55675         if(Roo.isSafari){
55676             this.updateHeaders();
55677         }
55678         this.updateSplitters();
55679         this.layout();
55680     },
55681
55682     unhideColumn : function(colIndex){
55683         var cid = this.getColumnId(colIndex);
55684         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55685         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55686
55687         if(Roo.isSafari){
55688             this.updateHeaders();
55689         }
55690         this.updateSplitters();
55691         this.layout();
55692     },
55693
55694     insertRows : function(dm, firstRow, lastRow, isUpdate){
55695         if(firstRow == 0 && lastRow == dm.getCount()-1){
55696             this.refresh();
55697         }else{
55698             if(!isUpdate){
55699                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55700             }
55701             var s = this.getScrollState();
55702             var markup = this.renderRows(firstRow, lastRow);
55703             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55704             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55705             this.restoreScroll(s);
55706             if(!isUpdate){
55707                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55708                 this.syncRowHeights(firstRow, lastRow);
55709                 this.stripeRows(firstRow);
55710                 this.layout();
55711             }
55712         }
55713     },
55714
55715     bufferRows : function(markup, target, index){
55716         var before = null, trows = target.rows, tbody = target.tBodies[0];
55717         if(index < trows.length){
55718             before = trows[index];
55719         }
55720         var b = document.createElement("div");
55721         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55722         var rows = b.firstChild.rows;
55723         for(var i = 0, len = rows.length; i < len; i++){
55724             if(before){
55725                 tbody.insertBefore(rows[0], before);
55726             }else{
55727                 tbody.appendChild(rows[0]);
55728             }
55729         }
55730         b.innerHTML = "";
55731         b = null;
55732     },
55733
55734     deleteRows : function(dm, firstRow, lastRow){
55735         if(dm.getRowCount()<1){
55736             this.fireEvent("beforerefresh", this);
55737             this.mainBody.update("");
55738             this.lockedBody.update("");
55739             this.fireEvent("refresh", this);
55740         }else{
55741             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55742             var bt = this.getBodyTable();
55743             var tbody = bt.firstChild;
55744             var rows = bt.rows;
55745             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55746                 tbody.removeChild(rows[firstRow]);
55747             }
55748             this.stripeRows(firstRow);
55749             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55750         }
55751     },
55752
55753     updateRows : function(dataSource, firstRow, lastRow){
55754         var s = this.getScrollState();
55755         this.refresh();
55756         this.restoreScroll(s);
55757     },
55758
55759     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55760         if(!noRefresh){
55761            this.refresh();
55762         }
55763         this.updateHeaderSortState();
55764     },
55765
55766     getScrollState : function(){
55767         
55768         var sb = this.scroller.dom;
55769         return {left: sb.scrollLeft, top: sb.scrollTop};
55770     },
55771
55772     stripeRows : function(startRow){
55773         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55774             return;
55775         }
55776         startRow = startRow || 0;
55777         var rows = this.getBodyTable().rows;
55778         var lrows = this.getLockedTable().rows;
55779         var cls = ' x-grid-row-alt ';
55780         for(var i = startRow, len = rows.length; i < len; i++){
55781             var row = rows[i], lrow = lrows[i];
55782             var isAlt = ((i+1) % 2 == 0);
55783             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55784             if(isAlt == hasAlt){
55785                 continue;
55786             }
55787             if(isAlt){
55788                 row.className += " x-grid-row-alt";
55789             }else{
55790                 row.className = row.className.replace("x-grid-row-alt", "");
55791             }
55792             if(lrow){
55793                 lrow.className = row.className;
55794             }
55795         }
55796     },
55797
55798     restoreScroll : function(state){
55799         //Roo.log('GridView.restoreScroll');
55800         var sb = this.scroller.dom;
55801         sb.scrollLeft = state.left;
55802         sb.scrollTop = state.top;
55803         this.syncScroll();
55804     },
55805
55806     syncScroll : function(){
55807         //Roo.log('GridView.syncScroll');
55808         var sb = this.scroller.dom;
55809         var sh = this.mainHd.dom;
55810         var bs = this.mainBody.dom;
55811         var lv = this.lockedBody.dom;
55812         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55813         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55814     },
55815
55816     handleScroll : function(e){
55817         this.syncScroll();
55818         var sb = this.scroller.dom;
55819         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55820         e.stopEvent();
55821     },
55822
55823     handleWheel : function(e){
55824         var d = e.getWheelDelta();
55825         this.scroller.dom.scrollTop -= d*22;
55826         // set this here to prevent jumpy scrolling on large tables
55827         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55828         e.stopEvent();
55829     },
55830
55831     renderRows : function(startRow, endRow){
55832         // pull in all the crap needed to render rows
55833         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55834         var colCount = cm.getColumnCount();
55835
55836         if(ds.getCount() < 1){
55837             return ["", ""];
55838         }
55839
55840         // build a map for all the columns
55841         var cs = [];
55842         for(var i = 0; i < colCount; i++){
55843             var name = cm.getDataIndex(i);
55844             cs[i] = {
55845                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55846                 renderer : cm.getRenderer(i),
55847                 id : cm.getColumnId(i),
55848                 locked : cm.isLocked(i),
55849                 has_editor : cm.isCellEditable(i)
55850             };
55851         }
55852
55853         startRow = startRow || 0;
55854         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55855
55856         // records to render
55857         var rs = ds.getRange(startRow, endRow);
55858
55859         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55860     },
55861
55862     // As much as I hate to duplicate code, this was branched because FireFox really hates
55863     // [].join("") on strings. The performance difference was substantial enough to
55864     // branch this function
55865     doRender : Roo.isGecko ?
55866             function(cs, rs, ds, startRow, colCount, stripe){
55867                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55868                 // buffers
55869                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55870                 
55871                 var hasListener = this.grid.hasListener('rowclass');
55872                 var rowcfg = {};
55873                 for(var j = 0, len = rs.length; j < len; j++){
55874                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55875                     for(var i = 0; i < colCount; i++){
55876                         c = cs[i];
55877                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55878                         p.id = c.id;
55879                         p.css = p.attr = "";
55880                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55881                         if(p.value == undefined || p.value === "") {
55882                             p.value = "&#160;";
55883                         }
55884                         if(c.has_editor){
55885                             p.css += ' x-grid-editable-cell';
55886                         }
55887                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55888                             p.css +=  ' x-grid-dirty-cell';
55889                         }
55890                         var markup = ct.apply(p);
55891                         if(!c.locked){
55892                             cb+= markup;
55893                         }else{
55894                             lcb+= markup;
55895                         }
55896                     }
55897                     var alt = [];
55898                     if(stripe && ((rowIndex+1) % 2 == 0)){
55899                         alt.push("x-grid-row-alt")
55900                     }
55901                     if(r.dirty){
55902                         alt.push(  " x-grid-dirty-row");
55903                     }
55904                     rp.cells = lcb;
55905                     if(this.getRowClass){
55906                         alt.push(this.getRowClass(r, rowIndex));
55907                     }
55908                     if (hasListener) {
55909                         rowcfg = {
55910                              
55911                             record: r,
55912                             rowIndex : rowIndex,
55913                             rowClass : ''
55914                         };
55915                         this.grid.fireEvent('rowclass', this, rowcfg);
55916                         alt.push(rowcfg.rowClass);
55917                     }
55918                     rp.alt = alt.join(" ");
55919                     lbuf+= rt.apply(rp);
55920                     rp.cells = cb;
55921                     buf+=  rt.apply(rp);
55922                 }
55923                 return [lbuf, buf];
55924             } :
55925             function(cs, rs, ds, startRow, colCount, stripe){
55926                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55927                 // buffers
55928                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55929                 var hasListener = this.grid.hasListener('rowclass');
55930  
55931                 var rowcfg = {};
55932                 for(var j = 0, len = rs.length; j < len; j++){
55933                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55934                     for(var i = 0; i < colCount; i++){
55935                         c = cs[i];
55936                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55937                         p.id = c.id;
55938                         p.css = p.attr = "";
55939                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55940                         if(p.value == undefined || p.value === "") {
55941                             p.value = "&#160;";
55942                         }
55943                         //Roo.log(c);
55944                          if(c.has_editor){
55945                             p.css += ' x-grid-editable-cell';
55946                         }
55947                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55948                             p.css += ' x-grid-dirty-cell' 
55949                         }
55950                         
55951                         var markup = ct.apply(p);
55952                         if(!c.locked){
55953                             cb[cb.length] = markup;
55954                         }else{
55955                             lcb[lcb.length] = markup;
55956                         }
55957                     }
55958                     var alt = [];
55959                     if(stripe && ((rowIndex+1) % 2 == 0)){
55960                         alt.push( "x-grid-row-alt");
55961                     }
55962                     if(r.dirty){
55963                         alt.push(" x-grid-dirty-row");
55964                     }
55965                     rp.cells = lcb;
55966                     if(this.getRowClass){
55967                         alt.push( this.getRowClass(r, rowIndex));
55968                     }
55969                     if (hasListener) {
55970                         rowcfg = {
55971                              
55972                             record: r,
55973                             rowIndex : rowIndex,
55974                             rowClass : ''
55975                         };
55976                         this.grid.fireEvent('rowclass', this, rowcfg);
55977                         alt.push(rowcfg.rowClass);
55978                     }
55979                     
55980                     rp.alt = alt.join(" ");
55981                     rp.cells = lcb.join("");
55982                     lbuf[lbuf.length] = rt.apply(rp);
55983                     rp.cells = cb.join("");
55984                     buf[buf.length] =  rt.apply(rp);
55985                 }
55986                 return [lbuf.join(""), buf.join("")];
55987             },
55988
55989     renderBody : function(){
55990         var markup = this.renderRows();
55991         var bt = this.templates.body;
55992         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55993     },
55994
55995     /**
55996      * Refreshes the grid
55997      * @param {Boolean} headersToo
55998      */
55999     refresh : function(headersToo){
56000         this.fireEvent("beforerefresh", this);
56001         this.grid.stopEditing();
56002         var result = this.renderBody();
56003         this.lockedBody.update(result[0]);
56004         this.mainBody.update(result[1]);
56005         if(headersToo === true){
56006             this.updateHeaders();
56007             this.updateColumns();
56008             this.updateSplitters();
56009             this.updateHeaderSortState();
56010         }
56011         this.syncRowHeights();
56012         this.layout();
56013         this.fireEvent("refresh", this);
56014     },
56015
56016     handleColumnMove : function(cm, oldIndex, newIndex){
56017         this.indexMap = null;
56018         var s = this.getScrollState();
56019         this.refresh(true);
56020         this.restoreScroll(s);
56021         this.afterMove(newIndex);
56022     },
56023
56024     afterMove : function(colIndex){
56025         if(this.enableMoveAnim && Roo.enableFx){
56026             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56027         }
56028         // if multisort - fix sortOrder, and reload..
56029         if (this.grid.dataSource.multiSort) {
56030             // the we can call sort again..
56031             var dm = this.grid.dataSource;
56032             var cm = this.grid.colModel;
56033             var so = [];
56034             for(var i = 0; i < cm.config.length; i++ ) {
56035                 
56036                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56037                     continue; // dont' bother, it's not in sort list or being set.
56038                 }
56039                 
56040                 so.push(cm.config[i].dataIndex);
56041             };
56042             dm.sortOrder = so;
56043             dm.load(dm.lastOptions);
56044             
56045             
56046         }
56047         
56048     },
56049
56050     updateCell : function(dm, rowIndex, dataIndex){
56051         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56052         if(typeof colIndex == "undefined"){ // not present in grid
56053             return;
56054         }
56055         var cm = this.grid.colModel;
56056         var cell = this.getCell(rowIndex, colIndex);
56057         var cellText = this.getCellText(rowIndex, colIndex);
56058
56059         var p = {
56060             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56061             id : cm.getColumnId(colIndex),
56062             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56063         };
56064         var renderer = cm.getRenderer(colIndex);
56065         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56066         if(typeof val == "undefined" || val === "") {
56067             val = "&#160;";
56068         }
56069         cellText.innerHTML = val;
56070         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56071         this.syncRowHeights(rowIndex, rowIndex);
56072     },
56073
56074     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56075         var maxWidth = 0;
56076         if(this.grid.autoSizeHeaders){
56077             var h = this.getHeaderCellMeasure(colIndex);
56078             maxWidth = Math.max(maxWidth, h.scrollWidth);
56079         }
56080         var tb, index;
56081         if(this.cm.isLocked(colIndex)){
56082             tb = this.getLockedTable();
56083             index = colIndex;
56084         }else{
56085             tb = this.getBodyTable();
56086             index = colIndex - this.cm.getLockedCount();
56087         }
56088         if(tb && tb.rows){
56089             var rows = tb.rows;
56090             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56091             for(var i = 0; i < stopIndex; i++){
56092                 var cell = rows[i].childNodes[index].firstChild;
56093                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56094             }
56095         }
56096         return maxWidth + /*margin for error in IE*/ 5;
56097     },
56098     /**
56099      * Autofit a column to its content.
56100      * @param {Number} colIndex
56101      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56102      */
56103      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56104          if(this.cm.isHidden(colIndex)){
56105              return; // can't calc a hidden column
56106          }
56107         if(forceMinSize){
56108             var cid = this.cm.getColumnId(colIndex);
56109             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56110            if(this.grid.autoSizeHeaders){
56111                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56112            }
56113         }
56114         var newWidth = this.calcColumnWidth(colIndex);
56115         this.cm.setColumnWidth(colIndex,
56116             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56117         if(!suppressEvent){
56118             this.grid.fireEvent("columnresize", colIndex, newWidth);
56119         }
56120     },
56121
56122     /**
56123      * Autofits all columns to their content and then expands to fit any extra space in the grid
56124      */
56125      autoSizeColumns : function(){
56126         var cm = this.grid.colModel;
56127         var colCount = cm.getColumnCount();
56128         for(var i = 0; i < colCount; i++){
56129             this.autoSizeColumn(i, true, true);
56130         }
56131         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56132             this.fitColumns();
56133         }else{
56134             this.updateColumns();
56135             this.layout();
56136         }
56137     },
56138
56139     /**
56140      * Autofits all columns to the grid's width proportionate with their current size
56141      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56142      */
56143     fitColumns : function(reserveScrollSpace){
56144         var cm = this.grid.colModel;
56145         var colCount = cm.getColumnCount();
56146         var cols = [];
56147         var width = 0;
56148         var i, w;
56149         for (i = 0; i < colCount; i++){
56150             if(!cm.isHidden(i) && !cm.isFixed(i)){
56151                 w = cm.getColumnWidth(i);
56152                 cols.push(i);
56153                 cols.push(w);
56154                 width += w;
56155             }
56156         }
56157         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56158         if(reserveScrollSpace){
56159             avail -= 17;
56160         }
56161         var frac = (avail - cm.getTotalWidth())/width;
56162         while (cols.length){
56163             w = cols.pop();
56164             i = cols.pop();
56165             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56166         }
56167         this.updateColumns();
56168         this.layout();
56169     },
56170
56171     onRowSelect : function(rowIndex){
56172         var row = this.getRowComposite(rowIndex);
56173         row.addClass("x-grid-row-selected");
56174     },
56175
56176     onRowDeselect : function(rowIndex){
56177         var row = this.getRowComposite(rowIndex);
56178         row.removeClass("x-grid-row-selected");
56179     },
56180
56181     onCellSelect : function(row, col){
56182         var cell = this.getCell(row, col);
56183         if(cell){
56184             Roo.fly(cell).addClass("x-grid-cell-selected");
56185         }
56186     },
56187
56188     onCellDeselect : function(row, col){
56189         var cell = this.getCell(row, col);
56190         if(cell){
56191             Roo.fly(cell).removeClass("x-grid-cell-selected");
56192         }
56193     },
56194
56195     updateHeaderSortState : function(){
56196         
56197         // sort state can be single { field: xxx, direction : yyy}
56198         // or   { xxx=>ASC , yyy : DESC ..... }
56199         
56200         var mstate = {};
56201         if (!this.ds.multiSort) { 
56202             var state = this.ds.getSortState();
56203             if(!state){
56204                 return;
56205             }
56206             mstate[state.field] = state.direction;
56207             // FIXME... - this is not used here.. but might be elsewhere..
56208             this.sortState = state;
56209             
56210         } else {
56211             mstate = this.ds.sortToggle;
56212         }
56213         //remove existing sort classes..
56214         
56215         var sc = this.sortClasses;
56216         var hds = this.el.select(this.headerSelector).removeClass(sc);
56217         
56218         for(var f in mstate) {
56219         
56220             var sortColumn = this.cm.findColumnIndex(f);
56221             
56222             if(sortColumn != -1){
56223                 var sortDir = mstate[f];        
56224                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56225             }
56226         }
56227         
56228          
56229         
56230     },
56231
56232
56233     handleHeaderClick : function(g, index,e){
56234         
56235         Roo.log("header click");
56236         
56237         if (Roo.isTouch) {
56238             // touch events on header are handled by context
56239             this.handleHdCtx(g,index,e);
56240             return;
56241         }
56242         
56243         
56244         if(this.headersDisabled){
56245             return;
56246         }
56247         var dm = g.dataSource, cm = g.colModel;
56248         if(!cm.isSortable(index)){
56249             return;
56250         }
56251         g.stopEditing();
56252         
56253         if (dm.multiSort) {
56254             // update the sortOrder
56255             var so = [];
56256             for(var i = 0; i < cm.config.length; i++ ) {
56257                 
56258                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56259                     continue; // dont' bother, it's not in sort list or being set.
56260                 }
56261                 
56262                 so.push(cm.config[i].dataIndex);
56263             };
56264             dm.sortOrder = so;
56265         }
56266         
56267         
56268         dm.sort(cm.getDataIndex(index));
56269     },
56270
56271
56272     destroy : function(){
56273         if(this.colMenu){
56274             this.colMenu.removeAll();
56275             Roo.menu.MenuMgr.unregister(this.colMenu);
56276             this.colMenu.getEl().remove();
56277             delete this.colMenu;
56278         }
56279         if(this.hmenu){
56280             this.hmenu.removeAll();
56281             Roo.menu.MenuMgr.unregister(this.hmenu);
56282             this.hmenu.getEl().remove();
56283             delete this.hmenu;
56284         }
56285         if(this.grid.enableColumnMove){
56286             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56287             if(dds){
56288                 for(var dd in dds){
56289                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56290                         var elid = dds[dd].dragElId;
56291                         dds[dd].unreg();
56292                         Roo.get(elid).remove();
56293                     } else if(dds[dd].config.isTarget){
56294                         dds[dd].proxyTop.remove();
56295                         dds[dd].proxyBottom.remove();
56296                         dds[dd].unreg();
56297                     }
56298                     if(Roo.dd.DDM.locationCache[dd]){
56299                         delete Roo.dd.DDM.locationCache[dd];
56300                     }
56301                 }
56302                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56303             }
56304         }
56305         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56306         this.bind(null, null);
56307         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56308     },
56309
56310     handleLockChange : function(){
56311         this.refresh(true);
56312     },
56313
56314     onDenyColumnLock : function(){
56315
56316     },
56317
56318     onDenyColumnHide : function(){
56319
56320     },
56321
56322     handleHdMenuClick : function(item){
56323         var index = this.hdCtxIndex;
56324         var cm = this.cm, ds = this.ds;
56325         switch(item.id){
56326             case "asc":
56327                 ds.sort(cm.getDataIndex(index), "ASC");
56328                 break;
56329             case "desc":
56330                 ds.sort(cm.getDataIndex(index), "DESC");
56331                 break;
56332             case "lock":
56333                 var lc = cm.getLockedCount();
56334                 if(cm.getColumnCount(true) <= lc+1){
56335                     this.onDenyColumnLock();
56336                     return;
56337                 }
56338                 if(lc != index){
56339                     cm.setLocked(index, true, true);
56340                     cm.moveColumn(index, lc);
56341                     this.grid.fireEvent("columnmove", index, lc);
56342                 }else{
56343                     cm.setLocked(index, true);
56344                 }
56345             break;
56346             case "unlock":
56347                 var lc = cm.getLockedCount();
56348                 if((lc-1) != index){
56349                     cm.setLocked(index, false, true);
56350                     cm.moveColumn(index, lc-1);
56351                     this.grid.fireEvent("columnmove", index, lc-1);
56352                 }else{
56353                     cm.setLocked(index, false);
56354                 }
56355             break;
56356             case 'wider': // used to expand cols on touch..
56357             case 'narrow':
56358                 var cw = cm.getColumnWidth(index);
56359                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56360                 cw = Math.max(0, cw);
56361                 cw = Math.min(cw,4000);
56362                 cm.setColumnWidth(index, cw);
56363                 break;
56364                 
56365             default:
56366                 index = cm.getIndexById(item.id.substr(4));
56367                 if(index != -1){
56368                     if(item.checked && cm.getColumnCount(true) <= 1){
56369                         this.onDenyColumnHide();
56370                         return false;
56371                     }
56372                     cm.setHidden(index, item.checked);
56373                 }
56374         }
56375         return true;
56376     },
56377
56378     beforeColMenuShow : function(){
56379         var cm = this.cm,  colCount = cm.getColumnCount();
56380         this.colMenu.removeAll();
56381         for(var i = 0; i < colCount; i++){
56382             this.colMenu.add(new Roo.menu.CheckItem({
56383                 id: "col-"+cm.getColumnId(i),
56384                 text: cm.getColumnHeader(i),
56385                 checked: !cm.isHidden(i),
56386                 hideOnClick:false
56387             }));
56388         }
56389     },
56390
56391     handleHdCtx : function(g, index, e){
56392         e.stopEvent();
56393         var hd = this.getHeaderCell(index);
56394         this.hdCtxIndex = index;
56395         var ms = this.hmenu.items, cm = this.cm;
56396         ms.get("asc").setDisabled(!cm.isSortable(index));
56397         ms.get("desc").setDisabled(!cm.isSortable(index));
56398         if(this.grid.enableColLock !== false){
56399             ms.get("lock").setDisabled(cm.isLocked(index));
56400             ms.get("unlock").setDisabled(!cm.isLocked(index));
56401         }
56402         this.hmenu.show(hd, "tl-bl");
56403     },
56404
56405     handleHdOver : function(e){
56406         var hd = this.findHeaderCell(e.getTarget());
56407         if(hd && !this.headersDisabled){
56408             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56409                this.fly(hd).addClass("x-grid-hd-over");
56410             }
56411         }
56412     },
56413
56414     handleHdOut : function(e){
56415         var hd = this.findHeaderCell(e.getTarget());
56416         if(hd){
56417             this.fly(hd).removeClass("x-grid-hd-over");
56418         }
56419     },
56420
56421     handleSplitDblClick : function(e, t){
56422         var i = this.getCellIndex(t);
56423         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56424             this.autoSizeColumn(i, true);
56425             this.layout();
56426         }
56427     },
56428
56429     render : function(){
56430
56431         var cm = this.cm;
56432         var colCount = cm.getColumnCount();
56433
56434         if(this.grid.monitorWindowResize === true){
56435             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56436         }
56437         var header = this.renderHeaders();
56438         var body = this.templates.body.apply({rows:""});
56439         var html = this.templates.master.apply({
56440             lockedBody: body,
56441             body: body,
56442             lockedHeader: header[0],
56443             header: header[1]
56444         });
56445
56446         //this.updateColumns();
56447
56448         this.grid.getGridEl().dom.innerHTML = html;
56449
56450         this.initElements();
56451         
56452         // a kludge to fix the random scolling effect in webkit
56453         this.el.on("scroll", function() {
56454             this.el.dom.scrollTop=0; // hopefully not recursive..
56455         },this);
56456
56457         this.scroller.on("scroll", this.handleScroll, this);
56458         this.lockedBody.on("mousewheel", this.handleWheel, this);
56459         this.mainBody.on("mousewheel", this.handleWheel, this);
56460
56461         this.mainHd.on("mouseover", this.handleHdOver, this);
56462         this.mainHd.on("mouseout", this.handleHdOut, this);
56463         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56464                 {delegate: "."+this.splitClass});
56465
56466         this.lockedHd.on("mouseover", this.handleHdOver, this);
56467         this.lockedHd.on("mouseout", this.handleHdOut, this);
56468         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56469                 {delegate: "."+this.splitClass});
56470
56471         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56472             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56473         }
56474
56475         this.updateSplitters();
56476
56477         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56478             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56479             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56480         }
56481
56482         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56483             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56484             this.hmenu.add(
56485                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56486                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56487             );
56488             if(this.grid.enableColLock !== false){
56489                 this.hmenu.add('-',
56490                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56491                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56492                 );
56493             }
56494             if (Roo.isTouch) {
56495                  this.hmenu.add('-',
56496                     {id:"wider", text: this.columnsWiderText},
56497                     {id:"narrow", text: this.columnsNarrowText }
56498                 );
56499                 
56500                  
56501             }
56502             
56503             if(this.grid.enableColumnHide !== false){
56504
56505                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56506                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56507                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56508
56509                 this.hmenu.add('-',
56510                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56511                 );
56512             }
56513             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56514
56515             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56516         }
56517
56518         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56519             this.dd = new Roo.grid.GridDragZone(this.grid, {
56520                 ddGroup : this.grid.ddGroup || 'GridDD'
56521             });
56522             
56523         }
56524
56525         /*
56526         for(var i = 0; i < colCount; i++){
56527             if(cm.isHidden(i)){
56528                 this.hideColumn(i);
56529             }
56530             if(cm.config[i].align){
56531                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56532                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56533             }
56534         }*/
56535         
56536         this.updateHeaderSortState();
56537
56538         this.beforeInitialResize();
56539         this.layout(true);
56540
56541         // two part rendering gives faster view to the user
56542         this.renderPhase2.defer(1, this);
56543     },
56544
56545     renderPhase2 : function(){
56546         // render the rows now
56547         this.refresh();
56548         if(this.grid.autoSizeColumns){
56549             this.autoSizeColumns();
56550         }
56551     },
56552
56553     beforeInitialResize : function(){
56554
56555     },
56556
56557     onColumnSplitterMoved : function(i, w){
56558         this.userResized = true;
56559         var cm = this.grid.colModel;
56560         cm.setColumnWidth(i, w, true);
56561         var cid = cm.getColumnId(i);
56562         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56563         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56564         this.updateSplitters();
56565         this.layout();
56566         this.grid.fireEvent("columnresize", i, w);
56567     },
56568
56569     syncRowHeights : function(startIndex, endIndex){
56570         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56571             startIndex = startIndex || 0;
56572             var mrows = this.getBodyTable().rows;
56573             var lrows = this.getLockedTable().rows;
56574             var len = mrows.length-1;
56575             endIndex = Math.min(endIndex || len, len);
56576             for(var i = startIndex; i <= endIndex; i++){
56577                 var m = mrows[i], l = lrows[i];
56578                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56579                 m.style.height = l.style.height = h + "px";
56580             }
56581         }
56582     },
56583
56584     layout : function(initialRender, is2ndPass){
56585         var g = this.grid;
56586         var auto = g.autoHeight;
56587         var scrollOffset = 16;
56588         var c = g.getGridEl(), cm = this.cm,
56589                 expandCol = g.autoExpandColumn,
56590                 gv = this;
56591         //c.beginMeasure();
56592
56593         if(!c.dom.offsetWidth){ // display:none?
56594             if(initialRender){
56595                 this.lockedWrap.show();
56596                 this.mainWrap.show();
56597             }
56598             return;
56599         }
56600
56601         var hasLock = this.cm.isLocked(0);
56602
56603         var tbh = this.headerPanel.getHeight();
56604         var bbh = this.footerPanel.getHeight();
56605
56606         if(auto){
56607             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56608             var newHeight = ch + c.getBorderWidth("tb");
56609             if(g.maxHeight){
56610                 newHeight = Math.min(g.maxHeight, newHeight);
56611             }
56612             c.setHeight(newHeight);
56613         }
56614
56615         if(g.autoWidth){
56616             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56617         }
56618
56619         var s = this.scroller;
56620
56621         var csize = c.getSize(true);
56622
56623         this.el.setSize(csize.width, csize.height);
56624
56625         this.headerPanel.setWidth(csize.width);
56626         this.footerPanel.setWidth(csize.width);
56627
56628         var hdHeight = this.mainHd.getHeight();
56629         var vw = csize.width;
56630         var vh = csize.height - (tbh + bbh);
56631
56632         s.setSize(vw, vh);
56633
56634         var bt = this.getBodyTable();
56635         
56636         if(cm.getLockedCount() == cm.config.length){
56637             bt = this.getLockedTable();
56638         }
56639         
56640         var ltWidth = hasLock ?
56641                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56642
56643         var scrollHeight = bt.offsetHeight;
56644         var scrollWidth = ltWidth + bt.offsetWidth;
56645         var vscroll = false, hscroll = false;
56646
56647         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56648
56649         var lw = this.lockedWrap, mw = this.mainWrap;
56650         var lb = this.lockedBody, mb = this.mainBody;
56651
56652         setTimeout(function(){
56653             var t = s.dom.offsetTop;
56654             var w = s.dom.clientWidth,
56655                 h = s.dom.clientHeight;
56656
56657             lw.setTop(t);
56658             lw.setSize(ltWidth, h);
56659
56660             mw.setLeftTop(ltWidth, t);
56661             mw.setSize(w-ltWidth, h);
56662
56663             lb.setHeight(h-hdHeight);
56664             mb.setHeight(h-hdHeight);
56665
56666             if(is2ndPass !== true && !gv.userResized && expandCol){
56667                 // high speed resize without full column calculation
56668                 
56669                 var ci = cm.getIndexById(expandCol);
56670                 if (ci < 0) {
56671                     ci = cm.findColumnIndex(expandCol);
56672                 }
56673                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56674                 var expandId = cm.getColumnId(ci);
56675                 var  tw = cm.getTotalWidth(false);
56676                 var currentWidth = cm.getColumnWidth(ci);
56677                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56678                 if(currentWidth != cw){
56679                     cm.setColumnWidth(ci, cw, true);
56680                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56681                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56682                     gv.updateSplitters();
56683                     gv.layout(false, true);
56684                 }
56685             }
56686
56687             if(initialRender){
56688                 lw.show();
56689                 mw.show();
56690             }
56691             //c.endMeasure();
56692         }, 10);
56693     },
56694
56695     onWindowResize : function(){
56696         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56697             return;
56698         }
56699         this.layout();
56700     },
56701
56702     appendFooter : function(parentEl){
56703         return null;
56704     },
56705
56706     sortAscText : "Sort Ascending",
56707     sortDescText : "Sort Descending",
56708     lockText : "Lock Column",
56709     unlockText : "Unlock Column",
56710     columnsText : "Columns",
56711  
56712     columnsWiderText : "Wider",
56713     columnsNarrowText : "Thinner"
56714 });
56715
56716
56717 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56718     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56719     this.proxy.el.addClass('x-grid3-col-dd');
56720 };
56721
56722 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56723     handleMouseDown : function(e){
56724
56725     },
56726
56727     callHandleMouseDown : function(e){
56728         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56729     }
56730 });
56731 /*
56732  * Based on:
56733  * Ext JS Library 1.1.1
56734  * Copyright(c) 2006-2007, Ext JS, LLC.
56735  *
56736  * Originally Released Under LGPL - original licence link has changed is not relivant.
56737  *
56738  * Fork - LGPL
56739  * <script type="text/javascript">
56740  */
56741  
56742 // private
56743 // This is a support class used internally by the Grid components
56744 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56745     this.grid = grid;
56746     this.view = grid.getView();
56747     this.proxy = this.view.resizeProxy;
56748     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56749         "gridSplitters" + this.grid.getGridEl().id, {
56750         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56751     });
56752     this.setHandleElId(Roo.id(hd));
56753     this.setOuterHandleElId(Roo.id(hd2));
56754     this.scroll = false;
56755 };
56756 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56757     fly: Roo.Element.fly,
56758
56759     b4StartDrag : function(x, y){
56760         this.view.headersDisabled = true;
56761         this.proxy.setHeight(this.view.mainWrap.getHeight());
56762         var w = this.cm.getColumnWidth(this.cellIndex);
56763         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56764         this.resetConstraints();
56765         this.setXConstraint(minw, 1000);
56766         this.setYConstraint(0, 0);
56767         this.minX = x - minw;
56768         this.maxX = x + 1000;
56769         this.startPos = x;
56770         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56771     },
56772
56773
56774     handleMouseDown : function(e){
56775         ev = Roo.EventObject.setEvent(e);
56776         var t = this.fly(ev.getTarget());
56777         if(t.hasClass("x-grid-split")){
56778             this.cellIndex = this.view.getCellIndex(t.dom);
56779             this.split = t.dom;
56780             this.cm = this.grid.colModel;
56781             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56782                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56783             }
56784         }
56785     },
56786
56787     endDrag : function(e){
56788         this.view.headersDisabled = false;
56789         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56790         var diff = endX - this.startPos;
56791         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56792     },
56793
56794     autoOffset : function(){
56795         this.setDelta(0,0);
56796     }
56797 });/*
56798  * Based on:
56799  * Ext JS Library 1.1.1
56800  * Copyright(c) 2006-2007, Ext JS, LLC.
56801  *
56802  * Originally Released Under LGPL - original licence link has changed is not relivant.
56803  *
56804  * Fork - LGPL
56805  * <script type="text/javascript">
56806  */
56807  
56808 // private
56809 // This is a support class used internally by the Grid components
56810 Roo.grid.GridDragZone = function(grid, config){
56811     this.view = grid.getView();
56812     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56813     if(this.view.lockedBody){
56814         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56815         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56816     }
56817     this.scroll = false;
56818     this.grid = grid;
56819     this.ddel = document.createElement('div');
56820     this.ddel.className = 'x-grid-dd-wrap';
56821 };
56822
56823 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56824     ddGroup : "GridDD",
56825
56826     getDragData : function(e){
56827         var t = Roo.lib.Event.getTarget(e);
56828         var rowIndex = this.view.findRowIndex(t);
56829         var sm = this.grid.selModel;
56830             
56831         //Roo.log(rowIndex);
56832         
56833         if (sm.getSelectedCell) {
56834             // cell selection..
56835             if (!sm.getSelectedCell()) {
56836                 return false;
56837             }
56838             if (rowIndex != sm.getSelectedCell()[0]) {
56839                 return false;
56840             }
56841         
56842         }
56843         
56844         if(rowIndex !== false){
56845             
56846             // if editorgrid.. 
56847             
56848             
56849             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56850                
56851             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56852               //  
56853             //}
56854             if (e.hasModifier()){
56855                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56856             }
56857             
56858             Roo.log("getDragData");
56859             
56860             return {
56861                 grid: this.grid,
56862                 ddel: this.ddel,
56863                 rowIndex: rowIndex,
56864                 selections:sm.getSelections ? sm.getSelections() : (
56865                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56866                 )
56867             };
56868         }
56869         return false;
56870     },
56871
56872     onInitDrag : function(e){
56873         var data = this.dragData;
56874         this.ddel.innerHTML = this.grid.getDragDropText();
56875         this.proxy.update(this.ddel);
56876         // fire start drag?
56877     },
56878
56879     afterRepair : function(){
56880         this.dragging = false;
56881     },
56882
56883     getRepairXY : function(e, data){
56884         return false;
56885     },
56886
56887     onEndDrag : function(data, e){
56888         // fire end drag?
56889     },
56890
56891     onValidDrop : function(dd, e, id){
56892         // fire drag drop?
56893         this.hideProxy();
56894     },
56895
56896     beforeInvalidDrop : function(e, id){
56897
56898     }
56899 });/*
56900  * Based on:
56901  * Ext JS Library 1.1.1
56902  * Copyright(c) 2006-2007, Ext JS, LLC.
56903  *
56904  * Originally Released Under LGPL - original licence link has changed is not relivant.
56905  *
56906  * Fork - LGPL
56907  * <script type="text/javascript">
56908  */
56909  
56910
56911 /**
56912  * @class Roo.grid.ColumnModel
56913  * @extends Roo.util.Observable
56914  * This is the default implementation of a ColumnModel used by the Grid. It defines
56915  * the columns in the grid.
56916  * <br>Usage:<br>
56917  <pre><code>
56918  var colModel = new Roo.grid.ColumnModel([
56919         {header: "Ticker", width: 60, sortable: true, locked: true},
56920         {header: "Company Name", width: 150, sortable: true},
56921         {header: "Market Cap.", width: 100, sortable: true},
56922         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56923         {header: "Employees", width: 100, sortable: true, resizable: false}
56924  ]);
56925  </code></pre>
56926  * <p>
56927  
56928  * The config options listed for this class are options which may appear in each
56929  * individual column definition.
56930  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56931  * @constructor
56932  * @param {Object} config An Array of column config objects. See this class's
56933  * config objects for details.
56934 */
56935 Roo.grid.ColumnModel = function(config){
56936         /**
56937      * The config passed into the constructor
56938      */
56939     this.config = config;
56940     this.lookup = {};
56941
56942     // if no id, create one
56943     // if the column does not have a dataIndex mapping,
56944     // map it to the order it is in the config
56945     for(var i = 0, len = config.length; i < len; i++){
56946         var c = config[i];
56947         if(typeof c.dataIndex == "undefined"){
56948             c.dataIndex = i;
56949         }
56950         if(typeof c.renderer == "string"){
56951             c.renderer = Roo.util.Format[c.renderer];
56952         }
56953         if(typeof c.id == "undefined"){
56954             c.id = Roo.id();
56955         }
56956         if(c.editor && c.editor.xtype){
56957             c.editor  = Roo.factory(c.editor, Roo.grid);
56958         }
56959         if(c.editor && c.editor.isFormField){
56960             c.editor = new Roo.grid.GridEditor(c.editor);
56961         }
56962         this.lookup[c.id] = c;
56963     }
56964
56965     /**
56966      * The width of columns which have no width specified (defaults to 100)
56967      * @type Number
56968      */
56969     this.defaultWidth = 100;
56970
56971     /**
56972      * Default sortable of columns which have no sortable specified (defaults to false)
56973      * @type Boolean
56974      */
56975     this.defaultSortable = false;
56976
56977     this.addEvents({
56978         /**
56979              * @event widthchange
56980              * Fires when the width of a column changes.
56981              * @param {ColumnModel} this
56982              * @param {Number} columnIndex The column index
56983              * @param {Number} newWidth The new width
56984              */
56985             "widthchange": true,
56986         /**
56987              * @event headerchange
56988              * Fires when the text of a header changes.
56989              * @param {ColumnModel} this
56990              * @param {Number} columnIndex The column index
56991              * @param {Number} newText The new header text
56992              */
56993             "headerchange": true,
56994         /**
56995              * @event hiddenchange
56996              * Fires when a column is hidden or "unhidden".
56997              * @param {ColumnModel} this
56998              * @param {Number} columnIndex The column index
56999              * @param {Boolean} hidden true if hidden, false otherwise
57000              */
57001             "hiddenchange": true,
57002             /**
57003          * @event columnmoved
57004          * Fires when a column is moved.
57005          * @param {ColumnModel} this
57006          * @param {Number} oldIndex
57007          * @param {Number} newIndex
57008          */
57009         "columnmoved" : true,
57010         /**
57011          * @event columlockchange
57012          * Fires when a column's locked state is changed
57013          * @param {ColumnModel} this
57014          * @param {Number} colIndex
57015          * @param {Boolean} locked true if locked
57016          */
57017         "columnlockchange" : true
57018     });
57019     Roo.grid.ColumnModel.superclass.constructor.call(this);
57020 };
57021 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57022     /**
57023      * @cfg {String} header The header text to display in the Grid view.
57024      */
57025     /**
57026      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57027      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57028      * specified, the column's index is used as an index into the Record's data Array.
57029      */
57030     /**
57031      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57032      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57033      */
57034     /**
57035      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57036      * Defaults to the value of the {@link #defaultSortable} property.
57037      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57038      */
57039     /**
57040      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57041      */
57042     /**
57043      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57044      */
57045     /**
57046      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57047      */
57048     /**
57049      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57050      */
57051     /**
57052      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57053      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57054      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57055      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57056      */
57057        /**
57058      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57059      */
57060     /**
57061      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57062      */
57063     /**
57064      * @cfg {String} cursor (Optional)
57065      */
57066     /**
57067      * @cfg {String} tooltip (Optional)
57068      */
57069     /**
57070      * @cfg {Number} xs (Optional)
57071      */
57072     /**
57073      * @cfg {Number} sm (Optional)
57074      */
57075     /**
57076      * @cfg {Number} md (Optional)
57077      */
57078     /**
57079      * @cfg {Number} lg (Optional)
57080      */
57081     /**
57082      * Returns the id of the column at the specified index.
57083      * @param {Number} index The column index
57084      * @return {String} the id
57085      */
57086     getColumnId : function(index){
57087         return this.config[index].id;
57088     },
57089
57090     /**
57091      * Returns the column for a specified id.
57092      * @param {String} id The column id
57093      * @return {Object} the column
57094      */
57095     getColumnById : function(id){
57096         return this.lookup[id];
57097     },
57098
57099     
57100     /**
57101      * Returns the column for a specified dataIndex.
57102      * @param {String} dataIndex The column dataIndex
57103      * @return {Object|Boolean} the column or false if not found
57104      */
57105     getColumnByDataIndex: function(dataIndex){
57106         var index = this.findColumnIndex(dataIndex);
57107         return index > -1 ? this.config[index] : false;
57108     },
57109     
57110     /**
57111      * Returns the index for a specified column id.
57112      * @param {String} id The column id
57113      * @return {Number} the index, or -1 if not found
57114      */
57115     getIndexById : function(id){
57116         for(var i = 0, len = this.config.length; i < len; i++){
57117             if(this.config[i].id == id){
57118                 return i;
57119             }
57120         }
57121         return -1;
57122     },
57123     
57124     /**
57125      * Returns the index for a specified column dataIndex.
57126      * @param {String} dataIndex The column dataIndex
57127      * @return {Number} the index, or -1 if not found
57128      */
57129     
57130     findColumnIndex : function(dataIndex){
57131         for(var i = 0, len = this.config.length; i < len; i++){
57132             if(this.config[i].dataIndex == dataIndex){
57133                 return i;
57134             }
57135         }
57136         return -1;
57137     },
57138     
57139     
57140     moveColumn : function(oldIndex, newIndex){
57141         var c = this.config[oldIndex];
57142         this.config.splice(oldIndex, 1);
57143         this.config.splice(newIndex, 0, c);
57144         this.dataMap = null;
57145         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57146     },
57147
57148     isLocked : function(colIndex){
57149         return this.config[colIndex].locked === true;
57150     },
57151
57152     setLocked : function(colIndex, value, suppressEvent){
57153         if(this.isLocked(colIndex) == value){
57154             return;
57155         }
57156         this.config[colIndex].locked = value;
57157         if(!suppressEvent){
57158             this.fireEvent("columnlockchange", this, colIndex, value);
57159         }
57160     },
57161
57162     getTotalLockedWidth : function(){
57163         var totalWidth = 0;
57164         for(var i = 0; i < this.config.length; i++){
57165             if(this.isLocked(i) && !this.isHidden(i)){
57166                 this.totalWidth += this.getColumnWidth(i);
57167             }
57168         }
57169         return totalWidth;
57170     },
57171
57172     getLockedCount : function(){
57173         for(var i = 0, len = this.config.length; i < len; i++){
57174             if(!this.isLocked(i)){
57175                 return i;
57176             }
57177         }
57178         
57179         return this.config.length;
57180     },
57181
57182     /**
57183      * Returns the number of columns.
57184      * @return {Number}
57185      */
57186     getColumnCount : function(visibleOnly){
57187         if(visibleOnly === true){
57188             var c = 0;
57189             for(var i = 0, len = this.config.length; i < len; i++){
57190                 if(!this.isHidden(i)){
57191                     c++;
57192                 }
57193             }
57194             return c;
57195         }
57196         return this.config.length;
57197     },
57198
57199     /**
57200      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57201      * @param {Function} fn
57202      * @param {Object} scope (optional)
57203      * @return {Array} result
57204      */
57205     getColumnsBy : function(fn, scope){
57206         var r = [];
57207         for(var i = 0, len = this.config.length; i < len; i++){
57208             var c = this.config[i];
57209             if(fn.call(scope||this, c, i) === true){
57210                 r[r.length] = c;
57211             }
57212         }
57213         return r;
57214     },
57215
57216     /**
57217      * Returns true if the specified column is sortable.
57218      * @param {Number} col The column index
57219      * @return {Boolean}
57220      */
57221     isSortable : function(col){
57222         if(typeof this.config[col].sortable == "undefined"){
57223             return this.defaultSortable;
57224         }
57225         return this.config[col].sortable;
57226     },
57227
57228     /**
57229      * Returns the rendering (formatting) function defined for the column.
57230      * @param {Number} col The column index.
57231      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57232      */
57233     getRenderer : function(col){
57234         if(!this.config[col].renderer){
57235             return Roo.grid.ColumnModel.defaultRenderer;
57236         }
57237         return this.config[col].renderer;
57238     },
57239
57240     /**
57241      * Sets the rendering (formatting) function for a column.
57242      * @param {Number} col The column index
57243      * @param {Function} fn The function to use to process the cell's raw data
57244      * to return HTML markup for the grid view. The render function is called with
57245      * the following parameters:<ul>
57246      * <li>Data value.</li>
57247      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57248      * <li>css A CSS style string to apply to the table cell.</li>
57249      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57250      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57251      * <li>Row index</li>
57252      * <li>Column index</li>
57253      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57254      */
57255     setRenderer : function(col, fn){
57256         this.config[col].renderer = fn;
57257     },
57258
57259     /**
57260      * Returns the width for the specified column.
57261      * @param {Number} col The column index
57262      * @return {Number}
57263      */
57264     getColumnWidth : function(col){
57265         return this.config[col].width * 1 || this.defaultWidth;
57266     },
57267
57268     /**
57269      * Sets the width for a column.
57270      * @param {Number} col The column index
57271      * @param {Number} width The new width
57272      */
57273     setColumnWidth : function(col, width, suppressEvent){
57274         this.config[col].width = width;
57275         this.totalWidth = null;
57276         if(!suppressEvent){
57277              this.fireEvent("widthchange", this, col, width);
57278         }
57279     },
57280
57281     /**
57282      * Returns the total width of all columns.
57283      * @param {Boolean} includeHidden True to include hidden column widths
57284      * @return {Number}
57285      */
57286     getTotalWidth : function(includeHidden){
57287         if(!this.totalWidth){
57288             this.totalWidth = 0;
57289             for(var i = 0, len = this.config.length; i < len; i++){
57290                 if(includeHidden || !this.isHidden(i)){
57291                     this.totalWidth += this.getColumnWidth(i);
57292                 }
57293             }
57294         }
57295         return this.totalWidth;
57296     },
57297
57298     /**
57299      * Returns the header for the specified column.
57300      * @param {Number} col The column index
57301      * @return {String}
57302      */
57303     getColumnHeader : function(col){
57304         return this.config[col].header;
57305     },
57306
57307     /**
57308      * Sets the header for a column.
57309      * @param {Number} col The column index
57310      * @param {String} header The new header
57311      */
57312     setColumnHeader : function(col, header){
57313         this.config[col].header = header;
57314         this.fireEvent("headerchange", this, col, header);
57315     },
57316
57317     /**
57318      * Returns the tooltip for the specified column.
57319      * @param {Number} col The column index
57320      * @return {String}
57321      */
57322     getColumnTooltip : function(col){
57323             return this.config[col].tooltip;
57324     },
57325     /**
57326      * Sets the tooltip for a column.
57327      * @param {Number} col The column index
57328      * @param {String} tooltip The new tooltip
57329      */
57330     setColumnTooltip : function(col, tooltip){
57331             this.config[col].tooltip = tooltip;
57332     },
57333
57334     /**
57335      * Returns the dataIndex for the specified column.
57336      * @param {Number} col The column index
57337      * @return {Number}
57338      */
57339     getDataIndex : function(col){
57340         return this.config[col].dataIndex;
57341     },
57342
57343     /**
57344      * Sets the dataIndex for a column.
57345      * @param {Number} col The column index
57346      * @param {Number} dataIndex The new dataIndex
57347      */
57348     setDataIndex : function(col, dataIndex){
57349         this.config[col].dataIndex = dataIndex;
57350     },
57351
57352     
57353     
57354     /**
57355      * Returns true if the cell is editable.
57356      * @param {Number} colIndex The column index
57357      * @param {Number} rowIndex The row index - this is nto actually used..?
57358      * @return {Boolean}
57359      */
57360     isCellEditable : function(colIndex, rowIndex){
57361         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57362     },
57363
57364     /**
57365      * Returns the editor defined for the cell/column.
57366      * return false or null to disable editing.
57367      * @param {Number} colIndex The column index
57368      * @param {Number} rowIndex The row index
57369      * @return {Object}
57370      */
57371     getCellEditor : function(colIndex, rowIndex){
57372         return this.config[colIndex].editor;
57373     },
57374
57375     /**
57376      * Sets if a column is editable.
57377      * @param {Number} col The column index
57378      * @param {Boolean} editable True if the column is editable
57379      */
57380     setEditable : function(col, editable){
57381         this.config[col].editable = editable;
57382     },
57383
57384
57385     /**
57386      * Returns true if the column is hidden.
57387      * @param {Number} colIndex The column index
57388      * @return {Boolean}
57389      */
57390     isHidden : function(colIndex){
57391         return this.config[colIndex].hidden;
57392     },
57393
57394
57395     /**
57396      * Returns true if the column width cannot be changed
57397      */
57398     isFixed : function(colIndex){
57399         return this.config[colIndex].fixed;
57400     },
57401
57402     /**
57403      * Returns true if the column can be resized
57404      * @return {Boolean}
57405      */
57406     isResizable : function(colIndex){
57407         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57408     },
57409     /**
57410      * Sets if a column is hidden.
57411      * @param {Number} colIndex The column index
57412      * @param {Boolean} hidden True if the column is hidden
57413      */
57414     setHidden : function(colIndex, hidden){
57415         this.config[colIndex].hidden = hidden;
57416         this.totalWidth = null;
57417         this.fireEvent("hiddenchange", this, colIndex, hidden);
57418     },
57419
57420     /**
57421      * Sets the editor for a column.
57422      * @param {Number} col The column index
57423      * @param {Object} editor The editor object
57424      */
57425     setEditor : function(col, editor){
57426         this.config[col].editor = editor;
57427     }
57428 });
57429
57430 Roo.grid.ColumnModel.defaultRenderer = function(value)
57431 {
57432     if(typeof value == "object") {
57433         return value;
57434     }
57435         if(typeof value == "string" && value.length < 1){
57436             return "&#160;";
57437         }
57438     
57439         return String.format("{0}", value);
57440 };
57441
57442 // Alias for backwards compatibility
57443 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57444 /*
57445  * Based on:
57446  * Ext JS Library 1.1.1
57447  * Copyright(c) 2006-2007, Ext JS, LLC.
57448  *
57449  * Originally Released Under LGPL - original licence link has changed is not relivant.
57450  *
57451  * Fork - LGPL
57452  * <script type="text/javascript">
57453  */
57454
57455 /**
57456  * @class Roo.grid.AbstractSelectionModel
57457  * @extends Roo.util.Observable
57458  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57459  * implemented by descendant classes.  This class should not be directly instantiated.
57460  * @constructor
57461  */
57462 Roo.grid.AbstractSelectionModel = function(){
57463     this.locked = false;
57464     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57465 };
57466
57467 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57468     /** @ignore Called by the grid automatically. Do not call directly. */
57469     init : function(grid){
57470         this.grid = grid;
57471         this.initEvents();
57472     },
57473
57474     /**
57475      * Locks the selections.
57476      */
57477     lock : function(){
57478         this.locked = true;
57479     },
57480
57481     /**
57482      * Unlocks the selections.
57483      */
57484     unlock : function(){
57485         this.locked = false;
57486     },
57487
57488     /**
57489      * Returns true if the selections are locked.
57490      * @return {Boolean}
57491      */
57492     isLocked : function(){
57493         return this.locked;
57494     }
57495 });/*
57496  * Based on:
57497  * Ext JS Library 1.1.1
57498  * Copyright(c) 2006-2007, Ext JS, LLC.
57499  *
57500  * Originally Released Under LGPL - original licence link has changed is not relivant.
57501  *
57502  * Fork - LGPL
57503  * <script type="text/javascript">
57504  */
57505 /**
57506  * @extends Roo.grid.AbstractSelectionModel
57507  * @class Roo.grid.RowSelectionModel
57508  * The default SelectionModel used by {@link Roo.grid.Grid}.
57509  * It supports multiple selections and keyboard selection/navigation. 
57510  * @constructor
57511  * @param {Object} config
57512  */
57513 Roo.grid.RowSelectionModel = function(config){
57514     Roo.apply(this, config);
57515     this.selections = new Roo.util.MixedCollection(false, function(o){
57516         return o.id;
57517     });
57518
57519     this.last = false;
57520     this.lastActive = false;
57521
57522     this.addEvents({
57523         /**
57524              * @event selectionchange
57525              * Fires when the selection changes
57526              * @param {SelectionModel} this
57527              */
57528             "selectionchange" : true,
57529         /**
57530              * @event afterselectionchange
57531              * Fires after the selection changes (eg. by key press or clicking)
57532              * @param {SelectionModel} this
57533              */
57534             "afterselectionchange" : true,
57535         /**
57536              * @event beforerowselect
57537              * Fires when a row is selected being selected, return false to cancel.
57538              * @param {SelectionModel} this
57539              * @param {Number} rowIndex The selected index
57540              * @param {Boolean} keepExisting False if other selections will be cleared
57541              */
57542             "beforerowselect" : true,
57543         /**
57544              * @event rowselect
57545              * Fires when a row is selected.
57546              * @param {SelectionModel} this
57547              * @param {Number} rowIndex The selected index
57548              * @param {Roo.data.Record} r The record
57549              */
57550             "rowselect" : true,
57551         /**
57552              * @event rowdeselect
57553              * Fires when a row is deselected.
57554              * @param {SelectionModel} this
57555              * @param {Number} rowIndex The selected index
57556              */
57557         "rowdeselect" : true
57558     });
57559     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57560     this.locked = false;
57561 };
57562
57563 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57564     /**
57565      * @cfg {Boolean} singleSelect
57566      * True to allow selection of only one row at a time (defaults to false)
57567      */
57568     singleSelect : false,
57569
57570     // private
57571     initEvents : function(){
57572
57573         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57574             this.grid.on("mousedown", this.handleMouseDown, this);
57575         }else{ // allow click to work like normal
57576             this.grid.on("rowclick", this.handleDragableRowClick, this);
57577         }
57578
57579         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57580             "up" : function(e){
57581                 if(!e.shiftKey){
57582                     this.selectPrevious(e.shiftKey);
57583                 }else if(this.last !== false && this.lastActive !== false){
57584                     var last = this.last;
57585                     this.selectRange(this.last,  this.lastActive-1);
57586                     this.grid.getView().focusRow(this.lastActive);
57587                     if(last !== false){
57588                         this.last = last;
57589                     }
57590                 }else{
57591                     this.selectFirstRow();
57592                 }
57593                 this.fireEvent("afterselectionchange", this);
57594             },
57595             "down" : function(e){
57596                 if(!e.shiftKey){
57597                     this.selectNext(e.shiftKey);
57598                 }else if(this.last !== false && this.lastActive !== false){
57599                     var last = this.last;
57600                     this.selectRange(this.last,  this.lastActive+1);
57601                     this.grid.getView().focusRow(this.lastActive);
57602                     if(last !== false){
57603                         this.last = last;
57604                     }
57605                 }else{
57606                     this.selectFirstRow();
57607                 }
57608                 this.fireEvent("afterselectionchange", this);
57609             },
57610             scope: this
57611         });
57612
57613         var view = this.grid.view;
57614         view.on("refresh", this.onRefresh, this);
57615         view.on("rowupdated", this.onRowUpdated, this);
57616         view.on("rowremoved", this.onRemove, this);
57617     },
57618
57619     // private
57620     onRefresh : function(){
57621         var ds = this.grid.dataSource, i, v = this.grid.view;
57622         var s = this.selections;
57623         s.each(function(r){
57624             if((i = ds.indexOfId(r.id)) != -1){
57625                 v.onRowSelect(i);
57626                 s.add(ds.getAt(i)); // updating the selection relate data
57627             }else{
57628                 s.remove(r);
57629             }
57630         });
57631     },
57632
57633     // private
57634     onRemove : function(v, index, r){
57635         this.selections.remove(r);
57636     },
57637
57638     // private
57639     onRowUpdated : function(v, index, r){
57640         if(this.isSelected(r)){
57641             v.onRowSelect(index);
57642         }
57643     },
57644
57645     /**
57646      * Select records.
57647      * @param {Array} records The records to select
57648      * @param {Boolean} keepExisting (optional) True to keep existing selections
57649      */
57650     selectRecords : function(records, keepExisting){
57651         if(!keepExisting){
57652             this.clearSelections();
57653         }
57654         var ds = this.grid.dataSource;
57655         for(var i = 0, len = records.length; i < len; i++){
57656             this.selectRow(ds.indexOf(records[i]), true);
57657         }
57658     },
57659
57660     /**
57661      * Gets the number of selected rows.
57662      * @return {Number}
57663      */
57664     getCount : function(){
57665         return this.selections.length;
57666     },
57667
57668     /**
57669      * Selects the first row in the grid.
57670      */
57671     selectFirstRow : function(){
57672         this.selectRow(0);
57673     },
57674
57675     /**
57676      * Select the last row.
57677      * @param {Boolean} keepExisting (optional) True to keep existing selections
57678      */
57679     selectLastRow : function(keepExisting){
57680         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57681     },
57682
57683     /**
57684      * Selects the row immediately following the last selected row.
57685      * @param {Boolean} keepExisting (optional) True to keep existing selections
57686      */
57687     selectNext : function(keepExisting){
57688         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57689             this.selectRow(this.last+1, keepExisting);
57690             this.grid.getView().focusRow(this.last);
57691         }
57692     },
57693
57694     /**
57695      * Selects the row that precedes the last selected row.
57696      * @param {Boolean} keepExisting (optional) True to keep existing selections
57697      */
57698     selectPrevious : function(keepExisting){
57699         if(this.last){
57700             this.selectRow(this.last-1, keepExisting);
57701             this.grid.getView().focusRow(this.last);
57702         }
57703     },
57704
57705     /**
57706      * Returns the selected records
57707      * @return {Array} Array of selected records
57708      */
57709     getSelections : function(){
57710         return [].concat(this.selections.items);
57711     },
57712
57713     /**
57714      * Returns the first selected record.
57715      * @return {Record}
57716      */
57717     getSelected : function(){
57718         return this.selections.itemAt(0);
57719     },
57720
57721
57722     /**
57723      * Clears all selections.
57724      */
57725     clearSelections : function(fast){
57726         if(this.locked) {
57727             return;
57728         }
57729         if(fast !== true){
57730             var ds = this.grid.dataSource;
57731             var s = this.selections;
57732             s.each(function(r){
57733                 this.deselectRow(ds.indexOfId(r.id));
57734             }, this);
57735             s.clear();
57736         }else{
57737             this.selections.clear();
57738         }
57739         this.last = false;
57740     },
57741
57742
57743     /**
57744      * Selects all rows.
57745      */
57746     selectAll : function(){
57747         if(this.locked) {
57748             return;
57749         }
57750         this.selections.clear();
57751         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57752             this.selectRow(i, true);
57753         }
57754     },
57755
57756     /**
57757      * Returns True if there is a selection.
57758      * @return {Boolean}
57759      */
57760     hasSelection : function(){
57761         return this.selections.length > 0;
57762     },
57763
57764     /**
57765      * Returns True if the specified row is selected.
57766      * @param {Number/Record} record The record or index of the record to check
57767      * @return {Boolean}
57768      */
57769     isSelected : function(index){
57770         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57771         return (r && this.selections.key(r.id) ? true : false);
57772     },
57773
57774     /**
57775      * Returns True if the specified record id is selected.
57776      * @param {String} id The id of record to check
57777      * @return {Boolean}
57778      */
57779     isIdSelected : function(id){
57780         return (this.selections.key(id) ? true : false);
57781     },
57782
57783     // private
57784     handleMouseDown : function(e, t){
57785         var view = this.grid.getView(), rowIndex;
57786         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57787             return;
57788         };
57789         if(e.shiftKey && this.last !== false){
57790             var last = this.last;
57791             this.selectRange(last, rowIndex, e.ctrlKey);
57792             this.last = last; // reset the last
57793             view.focusRow(rowIndex);
57794         }else{
57795             var isSelected = this.isSelected(rowIndex);
57796             if(e.button !== 0 && isSelected){
57797                 view.focusRow(rowIndex);
57798             }else if(e.ctrlKey && isSelected){
57799                 this.deselectRow(rowIndex);
57800             }else if(!isSelected){
57801                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57802                 view.focusRow(rowIndex);
57803             }
57804         }
57805         this.fireEvent("afterselectionchange", this);
57806     },
57807     // private
57808     handleDragableRowClick :  function(grid, rowIndex, e) 
57809     {
57810         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57811             this.selectRow(rowIndex, false);
57812             grid.view.focusRow(rowIndex);
57813              this.fireEvent("afterselectionchange", this);
57814         }
57815     },
57816     
57817     /**
57818      * Selects multiple rows.
57819      * @param {Array} rows Array of the indexes of the row to select
57820      * @param {Boolean} keepExisting (optional) True to keep existing selections
57821      */
57822     selectRows : function(rows, keepExisting){
57823         if(!keepExisting){
57824             this.clearSelections();
57825         }
57826         for(var i = 0, len = rows.length; i < len; i++){
57827             this.selectRow(rows[i], true);
57828         }
57829     },
57830
57831     /**
57832      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57833      * @param {Number} startRow The index of the first row in the range
57834      * @param {Number} endRow The index of the last row in the range
57835      * @param {Boolean} keepExisting (optional) True to retain existing selections
57836      */
57837     selectRange : function(startRow, endRow, keepExisting){
57838         if(this.locked) {
57839             return;
57840         }
57841         if(!keepExisting){
57842             this.clearSelections();
57843         }
57844         if(startRow <= endRow){
57845             for(var i = startRow; i <= endRow; i++){
57846                 this.selectRow(i, true);
57847             }
57848         }else{
57849             for(var i = startRow; i >= endRow; i--){
57850                 this.selectRow(i, true);
57851             }
57852         }
57853     },
57854
57855     /**
57856      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57857      * @param {Number} startRow The index of the first row in the range
57858      * @param {Number} endRow The index of the last row in the range
57859      */
57860     deselectRange : function(startRow, endRow, preventViewNotify){
57861         if(this.locked) {
57862             return;
57863         }
57864         for(var i = startRow; i <= endRow; i++){
57865             this.deselectRow(i, preventViewNotify);
57866         }
57867     },
57868
57869     /**
57870      * Selects a row.
57871      * @param {Number} row The index of the row to select
57872      * @param {Boolean} keepExisting (optional) True to keep existing selections
57873      */
57874     selectRow : function(index, keepExisting, preventViewNotify){
57875         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57876             return;
57877         }
57878         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57879             if(!keepExisting || this.singleSelect){
57880                 this.clearSelections();
57881             }
57882             var r = this.grid.dataSource.getAt(index);
57883             this.selections.add(r);
57884             this.last = this.lastActive = index;
57885             if(!preventViewNotify){
57886                 this.grid.getView().onRowSelect(index);
57887             }
57888             this.fireEvent("rowselect", this, index, r);
57889             this.fireEvent("selectionchange", this);
57890         }
57891     },
57892
57893     /**
57894      * Deselects a row.
57895      * @param {Number} row The index of the row to deselect
57896      */
57897     deselectRow : function(index, preventViewNotify){
57898         if(this.locked) {
57899             return;
57900         }
57901         if(this.last == index){
57902             this.last = false;
57903         }
57904         if(this.lastActive == index){
57905             this.lastActive = false;
57906         }
57907         var r = this.grid.dataSource.getAt(index);
57908         this.selections.remove(r);
57909         if(!preventViewNotify){
57910             this.grid.getView().onRowDeselect(index);
57911         }
57912         this.fireEvent("rowdeselect", this, index);
57913         this.fireEvent("selectionchange", this);
57914     },
57915
57916     // private
57917     restoreLast : function(){
57918         if(this._last){
57919             this.last = this._last;
57920         }
57921     },
57922
57923     // private
57924     acceptsNav : function(row, col, cm){
57925         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57926     },
57927
57928     // private
57929     onEditorKey : function(field, e){
57930         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57931         if(k == e.TAB){
57932             e.stopEvent();
57933             ed.completeEdit();
57934             if(e.shiftKey){
57935                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57936             }else{
57937                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57938             }
57939         }else if(k == e.ENTER && !e.ctrlKey){
57940             e.stopEvent();
57941             ed.completeEdit();
57942             if(e.shiftKey){
57943                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57944             }else{
57945                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57946             }
57947         }else if(k == e.ESC){
57948             ed.cancelEdit();
57949         }
57950         if(newCell){
57951             g.startEditing(newCell[0], newCell[1]);
57952         }
57953     }
57954 });/*
57955  * Based on:
57956  * Ext JS Library 1.1.1
57957  * Copyright(c) 2006-2007, Ext JS, LLC.
57958  *
57959  * Originally Released Under LGPL - original licence link has changed is not relivant.
57960  *
57961  * Fork - LGPL
57962  * <script type="text/javascript">
57963  */
57964 /**
57965  * @class Roo.grid.CellSelectionModel
57966  * @extends Roo.grid.AbstractSelectionModel
57967  * This class provides the basic implementation for cell selection in a grid.
57968  * @constructor
57969  * @param {Object} config The object containing the configuration of this model.
57970  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57971  */
57972 Roo.grid.CellSelectionModel = function(config){
57973     Roo.apply(this, config);
57974
57975     this.selection = null;
57976
57977     this.addEvents({
57978         /**
57979              * @event beforerowselect
57980              * Fires before a cell is selected.
57981              * @param {SelectionModel} this
57982              * @param {Number} rowIndex The selected row index
57983              * @param {Number} colIndex The selected cell index
57984              */
57985             "beforecellselect" : true,
57986         /**
57987              * @event cellselect
57988              * Fires when a cell is selected.
57989              * @param {SelectionModel} this
57990              * @param {Number} rowIndex The selected row index
57991              * @param {Number} colIndex The selected cell index
57992              */
57993             "cellselect" : true,
57994         /**
57995              * @event selectionchange
57996              * Fires when the active selection changes.
57997              * @param {SelectionModel} this
57998              * @param {Object} selection null for no selection or an object (o) with two properties
57999                 <ul>
58000                 <li>o.record: the record object for the row the selection is in</li>
58001                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58002                 </ul>
58003              */
58004             "selectionchange" : true,
58005         /**
58006              * @event tabend
58007              * Fires when the tab (or enter) was pressed on the last editable cell
58008              * You can use this to trigger add new row.
58009              * @param {SelectionModel} this
58010              */
58011             "tabend" : true,
58012          /**
58013              * @event beforeeditnext
58014              * Fires before the next editable sell is made active
58015              * You can use this to skip to another cell or fire the tabend
58016              *    if you set cell to false
58017              * @param {Object} eventdata object : { cell : [ row, col ] } 
58018              */
58019             "beforeeditnext" : true
58020     });
58021     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58022 };
58023
58024 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58025     
58026     enter_is_tab: false,
58027
58028     /** @ignore */
58029     initEvents : function(){
58030         this.grid.on("mousedown", this.handleMouseDown, this);
58031         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58032         var view = this.grid.view;
58033         view.on("refresh", this.onViewChange, this);
58034         view.on("rowupdated", this.onRowUpdated, this);
58035         view.on("beforerowremoved", this.clearSelections, this);
58036         view.on("beforerowsinserted", this.clearSelections, this);
58037         if(this.grid.isEditor){
58038             this.grid.on("beforeedit", this.beforeEdit,  this);
58039         }
58040     },
58041
58042         //private
58043     beforeEdit : function(e){
58044         this.select(e.row, e.column, false, true, e.record);
58045     },
58046
58047         //private
58048     onRowUpdated : function(v, index, r){
58049         if(this.selection && this.selection.record == r){
58050             v.onCellSelect(index, this.selection.cell[1]);
58051         }
58052     },
58053
58054         //private
58055     onViewChange : function(){
58056         this.clearSelections(true);
58057     },
58058
58059         /**
58060          * Returns the currently selected cell,.
58061          * @return {Array} The selected cell (row, column) or null if none selected.
58062          */
58063     getSelectedCell : function(){
58064         return this.selection ? this.selection.cell : null;
58065     },
58066
58067     /**
58068      * Clears all selections.
58069      * @param {Boolean} true to prevent the gridview from being notified about the change.
58070      */
58071     clearSelections : function(preventNotify){
58072         var s = this.selection;
58073         if(s){
58074             if(preventNotify !== true){
58075                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58076             }
58077             this.selection = null;
58078             this.fireEvent("selectionchange", this, null);
58079         }
58080     },
58081
58082     /**
58083      * Returns true if there is a selection.
58084      * @return {Boolean}
58085      */
58086     hasSelection : function(){
58087         return this.selection ? true : false;
58088     },
58089
58090     /** @ignore */
58091     handleMouseDown : function(e, t){
58092         var v = this.grid.getView();
58093         if(this.isLocked()){
58094             return;
58095         };
58096         var row = v.findRowIndex(t);
58097         var cell = v.findCellIndex(t);
58098         if(row !== false && cell !== false){
58099             this.select(row, cell);
58100         }
58101     },
58102
58103     /**
58104      * Selects a cell.
58105      * @param {Number} rowIndex
58106      * @param {Number} collIndex
58107      */
58108     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58109         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58110             this.clearSelections();
58111             r = r || this.grid.dataSource.getAt(rowIndex);
58112             this.selection = {
58113                 record : r,
58114                 cell : [rowIndex, colIndex]
58115             };
58116             if(!preventViewNotify){
58117                 var v = this.grid.getView();
58118                 v.onCellSelect(rowIndex, colIndex);
58119                 if(preventFocus !== true){
58120                     v.focusCell(rowIndex, colIndex);
58121                 }
58122             }
58123             this.fireEvent("cellselect", this, rowIndex, colIndex);
58124             this.fireEvent("selectionchange", this, this.selection);
58125         }
58126     },
58127
58128         //private
58129     isSelectable : function(rowIndex, colIndex, cm){
58130         return !cm.isHidden(colIndex);
58131     },
58132
58133     /** @ignore */
58134     handleKeyDown : function(e){
58135         //Roo.log('Cell Sel Model handleKeyDown');
58136         if(!e.isNavKeyPress()){
58137             return;
58138         }
58139         var g = this.grid, s = this.selection;
58140         if(!s){
58141             e.stopEvent();
58142             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58143             if(cell){
58144                 this.select(cell[0], cell[1]);
58145             }
58146             return;
58147         }
58148         var sm = this;
58149         var walk = function(row, col, step){
58150             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58151         };
58152         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58153         var newCell;
58154
58155       
58156
58157         switch(k){
58158             case e.TAB:
58159                 // handled by onEditorKey
58160                 if (g.isEditor && g.editing) {
58161                     return;
58162                 }
58163                 if(e.shiftKey) {
58164                     newCell = walk(r, c-1, -1);
58165                 } else {
58166                     newCell = walk(r, c+1, 1);
58167                 }
58168                 break;
58169             
58170             case e.DOWN:
58171                newCell = walk(r+1, c, 1);
58172                 break;
58173             
58174             case e.UP:
58175                 newCell = walk(r-1, c, -1);
58176                 break;
58177             
58178             case e.RIGHT:
58179                 newCell = walk(r, c+1, 1);
58180                 break;
58181             
58182             case e.LEFT:
58183                 newCell = walk(r, c-1, -1);
58184                 break;
58185             
58186             case e.ENTER:
58187                 
58188                 if(g.isEditor && !g.editing){
58189                    g.startEditing(r, c);
58190                    e.stopEvent();
58191                    return;
58192                 }
58193                 
58194                 
58195              break;
58196         };
58197         if(newCell){
58198             this.select(newCell[0], newCell[1]);
58199             e.stopEvent();
58200             
58201         }
58202     },
58203
58204     acceptsNav : function(row, col, cm){
58205         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58206     },
58207     /**
58208      * Selects a cell.
58209      * @param {Number} field (not used) - as it's normally used as a listener
58210      * @param {Number} e - event - fake it by using
58211      *
58212      * var e = Roo.EventObjectImpl.prototype;
58213      * e.keyCode = e.TAB
58214      *
58215      * 
58216      */
58217     onEditorKey : function(field, e){
58218         
58219         var k = e.getKey(),
58220             newCell,
58221             g = this.grid,
58222             ed = g.activeEditor,
58223             forward = false;
58224         ///Roo.log('onEditorKey' + k);
58225         
58226         
58227         if (this.enter_is_tab && k == e.ENTER) {
58228             k = e.TAB;
58229         }
58230         
58231         if(k == e.TAB){
58232             if(e.shiftKey){
58233                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58234             }else{
58235                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58236                 forward = true;
58237             }
58238             
58239             e.stopEvent();
58240             
58241         } else if(k == e.ENTER &&  !e.ctrlKey){
58242             ed.completeEdit();
58243             e.stopEvent();
58244             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58245         
58246                 } else if(k == e.ESC){
58247             ed.cancelEdit();
58248         }
58249                 
58250         if (newCell) {
58251             var ecall = { cell : newCell, forward : forward };
58252             this.fireEvent('beforeeditnext', ecall );
58253             newCell = ecall.cell;
58254                         forward = ecall.forward;
58255         }
58256                 
58257         if(newCell){
58258             //Roo.log('next cell after edit');
58259             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58260         } else if (forward) {
58261             // tabbed past last
58262             this.fireEvent.defer(100, this, ['tabend',this]);
58263         }
58264     }
58265 });/*
58266  * Based on:
58267  * Ext JS Library 1.1.1
58268  * Copyright(c) 2006-2007, Ext JS, LLC.
58269  *
58270  * Originally Released Under LGPL - original licence link has changed is not relivant.
58271  *
58272  * Fork - LGPL
58273  * <script type="text/javascript">
58274  */
58275  
58276 /**
58277  * @class Roo.grid.EditorGrid
58278  * @extends Roo.grid.Grid
58279  * Class for creating and editable grid.
58280  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58281  * The container MUST have some type of size defined for the grid to fill. The container will be 
58282  * automatically set to position relative if it isn't already.
58283  * @param {Object} dataSource The data model to bind to
58284  * @param {Object} colModel The column model with info about this grid's columns
58285  */
58286 Roo.grid.EditorGrid = function(container, config){
58287     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58288     this.getGridEl().addClass("xedit-grid");
58289
58290     if(!this.selModel){
58291         this.selModel = new Roo.grid.CellSelectionModel();
58292     }
58293
58294     this.activeEditor = null;
58295
58296         this.addEvents({
58297             /**
58298              * @event beforeedit
58299              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58300              * <ul style="padding:5px;padding-left:16px;">
58301              * <li>grid - This grid</li>
58302              * <li>record - The record being edited</li>
58303              * <li>field - The field name being edited</li>
58304              * <li>value - The value for the field being edited.</li>
58305              * <li>row - The grid row index</li>
58306              * <li>column - The grid column index</li>
58307              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58308              * </ul>
58309              * @param {Object} e An edit event (see above for description)
58310              */
58311             "beforeedit" : true,
58312             /**
58313              * @event afteredit
58314              * Fires after a cell is edited. <br />
58315              * <ul style="padding:5px;padding-left:16px;">
58316              * <li>grid - This grid</li>
58317              * <li>record - The record being edited</li>
58318              * <li>field - The field name being edited</li>
58319              * <li>value - The value being set</li>
58320              * <li>originalValue - The original value for the field, before the edit.</li>
58321              * <li>row - The grid row index</li>
58322              * <li>column - The grid column index</li>
58323              * </ul>
58324              * @param {Object} e An edit event (see above for description)
58325              */
58326             "afteredit" : true,
58327             /**
58328              * @event validateedit
58329              * Fires after a cell is edited, but before the value is set in the record. 
58330          * You can use this to modify the value being set in the field, Return false
58331              * to cancel the change. The edit event object has the following properties <br />
58332              * <ul style="padding:5px;padding-left:16px;">
58333          * <li>editor - This editor</li>
58334              * <li>grid - This grid</li>
58335              * <li>record - The record being edited</li>
58336              * <li>field - The field name being edited</li>
58337              * <li>value - The value being set</li>
58338              * <li>originalValue - The original value for the field, before the edit.</li>
58339              * <li>row - The grid row index</li>
58340              * <li>column - The grid column index</li>
58341              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58342              * </ul>
58343              * @param {Object} e An edit event (see above for description)
58344              */
58345             "validateedit" : true
58346         });
58347     this.on("bodyscroll", this.stopEditing,  this);
58348     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58349 };
58350
58351 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58352     /**
58353      * @cfg {Number} clicksToEdit
58354      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58355      */
58356     clicksToEdit: 2,
58357
58358     // private
58359     isEditor : true,
58360     // private
58361     trackMouseOver: false, // causes very odd FF errors
58362
58363     onCellDblClick : function(g, row, col){
58364         this.startEditing(row, col);
58365     },
58366
58367     onEditComplete : function(ed, value, startValue){
58368         this.editing = false;
58369         this.activeEditor = null;
58370         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58371         var r = ed.record;
58372         var field = this.colModel.getDataIndex(ed.col);
58373         var e = {
58374             grid: this,
58375             record: r,
58376             field: field,
58377             originalValue: startValue,
58378             value: value,
58379             row: ed.row,
58380             column: ed.col,
58381             cancel:false,
58382             editor: ed
58383         };
58384         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58385         cell.show();
58386           
58387         if(String(value) !== String(startValue)){
58388             
58389             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58390                 r.set(field, e.value);
58391                 // if we are dealing with a combo box..
58392                 // then we also set the 'name' colum to be the displayField
58393                 if (ed.field.displayField && ed.field.name) {
58394                     r.set(ed.field.name, ed.field.el.dom.value);
58395                 }
58396                 
58397                 delete e.cancel; //?? why!!!
58398                 this.fireEvent("afteredit", e);
58399             }
58400         } else {
58401             this.fireEvent("afteredit", e); // always fire it!
58402         }
58403         this.view.focusCell(ed.row, ed.col);
58404     },
58405
58406     /**
58407      * Starts editing the specified for the specified row/column
58408      * @param {Number} rowIndex
58409      * @param {Number} colIndex
58410      */
58411     startEditing : function(row, col){
58412         this.stopEditing();
58413         if(this.colModel.isCellEditable(col, row)){
58414             this.view.ensureVisible(row, col, true);
58415           
58416             var r = this.dataSource.getAt(row);
58417             var field = this.colModel.getDataIndex(col);
58418             var cell = Roo.get(this.view.getCell(row,col));
58419             var e = {
58420                 grid: this,
58421                 record: r,
58422                 field: field,
58423                 value: r.data[field],
58424                 row: row,
58425                 column: col,
58426                 cancel:false 
58427             };
58428             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58429                 this.editing = true;
58430                 var ed = this.colModel.getCellEditor(col, row);
58431                 
58432                 if (!ed) {
58433                     return;
58434                 }
58435                 if(!ed.rendered){
58436                     ed.render(ed.parentEl || document.body);
58437                 }
58438                 ed.field.reset();
58439                
58440                 cell.hide();
58441                 
58442                 (function(){ // complex but required for focus issues in safari, ie and opera
58443                     ed.row = row;
58444                     ed.col = col;
58445                     ed.record = r;
58446                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58447                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58448                     this.activeEditor = ed;
58449                     var v = r.data[field];
58450                     ed.startEdit(this.view.getCell(row, col), v);
58451                     // combo's with 'displayField and name set
58452                     if (ed.field.displayField && ed.field.name) {
58453                         ed.field.el.dom.value = r.data[ed.field.name];
58454                     }
58455                     
58456                     
58457                 }).defer(50, this);
58458             }
58459         }
58460     },
58461         
58462     /**
58463      * Stops any active editing
58464      */
58465     stopEditing : function(){
58466         if(this.activeEditor){
58467             this.activeEditor.completeEdit();
58468         }
58469         this.activeEditor = null;
58470     },
58471         
58472          /**
58473      * Called to get grid's drag proxy text, by default returns this.ddText.
58474      * @return {String}
58475      */
58476     getDragDropText : function(){
58477         var count = this.selModel.getSelectedCell() ? 1 : 0;
58478         return String.format(this.ddText, count, count == 1 ? '' : 's');
58479     }
58480         
58481 });/*
58482  * Based on:
58483  * Ext JS Library 1.1.1
58484  * Copyright(c) 2006-2007, Ext JS, LLC.
58485  *
58486  * Originally Released Under LGPL - original licence link has changed is not relivant.
58487  *
58488  * Fork - LGPL
58489  * <script type="text/javascript">
58490  */
58491
58492 // private - not really -- you end up using it !
58493 // This is a support class used internally by the Grid components
58494
58495 /**
58496  * @class Roo.grid.GridEditor
58497  * @extends Roo.Editor
58498  * Class for creating and editable grid elements.
58499  * @param {Object} config any settings (must include field)
58500  */
58501 Roo.grid.GridEditor = function(field, config){
58502     if (!config && field.field) {
58503         config = field;
58504         field = Roo.factory(config.field, Roo.form);
58505     }
58506     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58507     field.monitorTab = false;
58508 };
58509
58510 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58511     
58512     /**
58513      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58514      */
58515     
58516     alignment: "tl-tl",
58517     autoSize: "width",
58518     hideEl : false,
58519     cls: "x-small-editor x-grid-editor",
58520     shim:false,
58521     shadow:"frame"
58522 });/*
58523  * Based on:
58524  * Ext JS Library 1.1.1
58525  * Copyright(c) 2006-2007, Ext JS, LLC.
58526  *
58527  * Originally Released Under LGPL - original licence link has changed is not relivant.
58528  *
58529  * Fork - LGPL
58530  * <script type="text/javascript">
58531  */
58532   
58533
58534   
58535 Roo.grid.PropertyRecord = Roo.data.Record.create([
58536     {name:'name',type:'string'},  'value'
58537 ]);
58538
58539
58540 Roo.grid.PropertyStore = function(grid, source){
58541     this.grid = grid;
58542     this.store = new Roo.data.Store({
58543         recordType : Roo.grid.PropertyRecord
58544     });
58545     this.store.on('update', this.onUpdate,  this);
58546     if(source){
58547         this.setSource(source);
58548     }
58549     Roo.grid.PropertyStore.superclass.constructor.call(this);
58550 };
58551
58552
58553
58554 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58555     setSource : function(o){
58556         this.source = o;
58557         this.store.removeAll();
58558         var data = [];
58559         for(var k in o){
58560             if(this.isEditableValue(o[k])){
58561                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58562             }
58563         }
58564         this.store.loadRecords({records: data}, {}, true);
58565     },
58566
58567     onUpdate : function(ds, record, type){
58568         if(type == Roo.data.Record.EDIT){
58569             var v = record.data['value'];
58570             var oldValue = record.modified['value'];
58571             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58572                 this.source[record.id] = v;
58573                 record.commit();
58574                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58575             }else{
58576                 record.reject();
58577             }
58578         }
58579     },
58580
58581     getProperty : function(row){
58582        return this.store.getAt(row);
58583     },
58584
58585     isEditableValue: function(val){
58586         if(val && val instanceof Date){
58587             return true;
58588         }else if(typeof val == 'object' || typeof val == 'function'){
58589             return false;
58590         }
58591         return true;
58592     },
58593
58594     setValue : function(prop, value){
58595         this.source[prop] = value;
58596         this.store.getById(prop).set('value', value);
58597     },
58598
58599     getSource : function(){
58600         return this.source;
58601     }
58602 });
58603
58604 Roo.grid.PropertyColumnModel = function(grid, store){
58605     this.grid = grid;
58606     var g = Roo.grid;
58607     g.PropertyColumnModel.superclass.constructor.call(this, [
58608         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58609         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58610     ]);
58611     this.store = store;
58612     this.bselect = Roo.DomHelper.append(document.body, {
58613         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58614             {tag: 'option', value: 'true', html: 'true'},
58615             {tag: 'option', value: 'false', html: 'false'}
58616         ]
58617     });
58618     Roo.id(this.bselect);
58619     var f = Roo.form;
58620     this.editors = {
58621         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58622         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58623         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58624         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58625         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58626     };
58627     this.renderCellDelegate = this.renderCell.createDelegate(this);
58628     this.renderPropDelegate = this.renderProp.createDelegate(this);
58629 };
58630
58631 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58632     
58633     
58634     nameText : 'Name',
58635     valueText : 'Value',
58636     
58637     dateFormat : 'm/j/Y',
58638     
58639     
58640     renderDate : function(dateVal){
58641         return dateVal.dateFormat(this.dateFormat);
58642     },
58643
58644     renderBool : function(bVal){
58645         return bVal ? 'true' : 'false';
58646     },
58647
58648     isCellEditable : function(colIndex, rowIndex){
58649         return colIndex == 1;
58650     },
58651
58652     getRenderer : function(col){
58653         return col == 1 ?
58654             this.renderCellDelegate : this.renderPropDelegate;
58655     },
58656
58657     renderProp : function(v){
58658         return this.getPropertyName(v);
58659     },
58660
58661     renderCell : function(val){
58662         var rv = val;
58663         if(val instanceof Date){
58664             rv = this.renderDate(val);
58665         }else if(typeof val == 'boolean'){
58666             rv = this.renderBool(val);
58667         }
58668         return Roo.util.Format.htmlEncode(rv);
58669     },
58670
58671     getPropertyName : function(name){
58672         var pn = this.grid.propertyNames;
58673         return pn && pn[name] ? pn[name] : name;
58674     },
58675
58676     getCellEditor : function(colIndex, rowIndex){
58677         var p = this.store.getProperty(rowIndex);
58678         var n = p.data['name'], val = p.data['value'];
58679         
58680         if(typeof(this.grid.customEditors[n]) == 'string'){
58681             return this.editors[this.grid.customEditors[n]];
58682         }
58683         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58684             return this.grid.customEditors[n];
58685         }
58686         if(val instanceof Date){
58687             return this.editors['date'];
58688         }else if(typeof val == 'number'){
58689             return this.editors['number'];
58690         }else if(typeof val == 'boolean'){
58691             return this.editors['boolean'];
58692         }else{
58693             return this.editors['string'];
58694         }
58695     }
58696 });
58697
58698 /**
58699  * @class Roo.grid.PropertyGrid
58700  * @extends Roo.grid.EditorGrid
58701  * This class represents the  interface of a component based property grid control.
58702  * <br><br>Usage:<pre><code>
58703  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58704       
58705  });
58706  // set any options
58707  grid.render();
58708  * </code></pre>
58709   
58710  * @constructor
58711  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58712  * The container MUST have some type of size defined for the grid to fill. The container will be
58713  * automatically set to position relative if it isn't already.
58714  * @param {Object} config A config object that sets properties on this grid.
58715  */
58716 Roo.grid.PropertyGrid = function(container, config){
58717     config = config || {};
58718     var store = new Roo.grid.PropertyStore(this);
58719     this.store = store;
58720     var cm = new Roo.grid.PropertyColumnModel(this, store);
58721     store.store.sort('name', 'ASC');
58722     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58723         ds: store.store,
58724         cm: cm,
58725         enableColLock:false,
58726         enableColumnMove:false,
58727         stripeRows:false,
58728         trackMouseOver: false,
58729         clicksToEdit:1
58730     }, config));
58731     this.getGridEl().addClass('x-props-grid');
58732     this.lastEditRow = null;
58733     this.on('columnresize', this.onColumnResize, this);
58734     this.addEvents({
58735          /**
58736              * @event beforepropertychange
58737              * Fires before a property changes (return false to stop?)
58738              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58739              * @param {String} id Record Id
58740              * @param {String} newval New Value
58741          * @param {String} oldval Old Value
58742              */
58743         "beforepropertychange": true,
58744         /**
58745              * @event propertychange
58746              * Fires after a property changes
58747              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58748              * @param {String} id Record Id
58749              * @param {String} newval New Value
58750          * @param {String} oldval Old Value
58751              */
58752         "propertychange": true
58753     });
58754     this.customEditors = this.customEditors || {};
58755 };
58756 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58757     
58758      /**
58759      * @cfg {Object} customEditors map of colnames=> custom editors.
58760      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58761      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58762      * false disables editing of the field.
58763          */
58764     
58765       /**
58766      * @cfg {Object} propertyNames map of property Names to their displayed value
58767          */
58768     
58769     render : function(){
58770         Roo.grid.PropertyGrid.superclass.render.call(this);
58771         this.autoSize.defer(100, this);
58772     },
58773
58774     autoSize : function(){
58775         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58776         if(this.view){
58777             this.view.fitColumns();
58778         }
58779     },
58780
58781     onColumnResize : function(){
58782         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58783         this.autoSize();
58784     },
58785     /**
58786      * Sets the data for the Grid
58787      * accepts a Key => Value object of all the elements avaiable.
58788      * @param {Object} data  to appear in grid.
58789      */
58790     setSource : function(source){
58791         this.store.setSource(source);
58792         //this.autoSize();
58793     },
58794     /**
58795      * Gets all the data from the grid.
58796      * @return {Object} data  data stored in grid
58797      */
58798     getSource : function(){
58799         return this.store.getSource();
58800     }
58801 });/*
58802   
58803  * Licence LGPL
58804  
58805  */
58806  
58807 /**
58808  * @class Roo.grid.Calendar
58809  * @extends Roo.util.Grid
58810  * This class extends the Grid to provide a calendar widget
58811  * <br><br>Usage:<pre><code>
58812  var grid = new Roo.grid.Calendar("my-container-id", {
58813      ds: myDataStore,
58814      cm: myColModel,
58815      selModel: mySelectionModel,
58816      autoSizeColumns: true,
58817      monitorWindowResize: false,
58818      trackMouseOver: true
58819      eventstore : real data store..
58820  });
58821  // set any options
58822  grid.render();
58823   
58824   * @constructor
58825  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58826  * The container MUST have some type of size defined for the grid to fill. The container will be
58827  * automatically set to position relative if it isn't already.
58828  * @param {Object} config A config object that sets properties on this grid.
58829  */
58830 Roo.grid.Calendar = function(container, config){
58831         // initialize the container
58832         this.container = Roo.get(container);
58833         this.container.update("");
58834         this.container.setStyle("overflow", "hidden");
58835     this.container.addClass('x-grid-container');
58836
58837     this.id = this.container.id;
58838
58839     Roo.apply(this, config);
58840     // check and correct shorthanded configs
58841     
58842     var rows = [];
58843     var d =1;
58844     for (var r = 0;r < 6;r++) {
58845         
58846         rows[r]=[];
58847         for (var c =0;c < 7;c++) {
58848             rows[r][c]= '';
58849         }
58850     }
58851     if (this.eventStore) {
58852         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58853         this.eventStore.on('load',this.onLoad, this);
58854         this.eventStore.on('beforeload',this.clearEvents, this);
58855          
58856     }
58857     
58858     this.dataSource = new Roo.data.Store({
58859             proxy: new Roo.data.MemoryProxy(rows),
58860             reader: new Roo.data.ArrayReader({}, [
58861                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58862     });
58863
58864     this.dataSource.load();
58865     this.ds = this.dataSource;
58866     this.ds.xmodule = this.xmodule || false;
58867     
58868     
58869     var cellRender = function(v,x,r)
58870     {
58871         return String.format(
58872             '<div class="fc-day  fc-widget-content"><div>' +
58873                 '<div class="fc-event-container"></div>' +
58874                 '<div class="fc-day-number">{0}</div>'+
58875                 
58876                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58877             '</div></div>', v);
58878     
58879     }
58880     
58881     
58882     this.colModel = new Roo.grid.ColumnModel( [
58883         {
58884             xtype: 'ColumnModel',
58885             xns: Roo.grid,
58886             dataIndex : 'weekday0',
58887             header : 'Sunday',
58888             renderer : cellRender
58889         },
58890         {
58891             xtype: 'ColumnModel',
58892             xns: Roo.grid,
58893             dataIndex : 'weekday1',
58894             header : 'Monday',
58895             renderer : cellRender
58896         },
58897         {
58898             xtype: 'ColumnModel',
58899             xns: Roo.grid,
58900             dataIndex : 'weekday2',
58901             header : 'Tuesday',
58902             renderer : cellRender
58903         },
58904         {
58905             xtype: 'ColumnModel',
58906             xns: Roo.grid,
58907             dataIndex : 'weekday3',
58908             header : 'Wednesday',
58909             renderer : cellRender
58910         },
58911         {
58912             xtype: 'ColumnModel',
58913             xns: Roo.grid,
58914             dataIndex : 'weekday4',
58915             header : 'Thursday',
58916             renderer : cellRender
58917         },
58918         {
58919             xtype: 'ColumnModel',
58920             xns: Roo.grid,
58921             dataIndex : 'weekday5',
58922             header : 'Friday',
58923             renderer : cellRender
58924         },
58925         {
58926             xtype: 'ColumnModel',
58927             xns: Roo.grid,
58928             dataIndex : 'weekday6',
58929             header : 'Saturday',
58930             renderer : cellRender
58931         }
58932     ]);
58933     this.cm = this.colModel;
58934     this.cm.xmodule = this.xmodule || false;
58935  
58936         
58937           
58938     //this.selModel = new Roo.grid.CellSelectionModel();
58939     //this.sm = this.selModel;
58940     //this.selModel.init(this);
58941     
58942     
58943     if(this.width){
58944         this.container.setWidth(this.width);
58945     }
58946
58947     if(this.height){
58948         this.container.setHeight(this.height);
58949     }
58950     /** @private */
58951         this.addEvents({
58952         // raw events
58953         /**
58954          * @event click
58955          * The raw click event for the entire grid.
58956          * @param {Roo.EventObject} e
58957          */
58958         "click" : true,
58959         /**
58960          * @event dblclick
58961          * The raw dblclick event for the entire grid.
58962          * @param {Roo.EventObject} e
58963          */
58964         "dblclick" : true,
58965         /**
58966          * @event contextmenu
58967          * The raw contextmenu event for the entire grid.
58968          * @param {Roo.EventObject} e
58969          */
58970         "contextmenu" : true,
58971         /**
58972          * @event mousedown
58973          * The raw mousedown event for the entire grid.
58974          * @param {Roo.EventObject} e
58975          */
58976         "mousedown" : true,
58977         /**
58978          * @event mouseup
58979          * The raw mouseup event for the entire grid.
58980          * @param {Roo.EventObject} e
58981          */
58982         "mouseup" : true,
58983         /**
58984          * @event mouseover
58985          * The raw mouseover event for the entire grid.
58986          * @param {Roo.EventObject} e
58987          */
58988         "mouseover" : true,
58989         /**
58990          * @event mouseout
58991          * The raw mouseout event for the entire grid.
58992          * @param {Roo.EventObject} e
58993          */
58994         "mouseout" : true,
58995         /**
58996          * @event keypress
58997          * The raw keypress event for the entire grid.
58998          * @param {Roo.EventObject} e
58999          */
59000         "keypress" : true,
59001         /**
59002          * @event keydown
59003          * The raw keydown event for the entire grid.
59004          * @param {Roo.EventObject} e
59005          */
59006         "keydown" : true,
59007
59008         // custom events
59009
59010         /**
59011          * @event cellclick
59012          * Fires when a cell is clicked
59013          * @param {Grid} this
59014          * @param {Number} rowIndex
59015          * @param {Number} columnIndex
59016          * @param {Roo.EventObject} e
59017          */
59018         "cellclick" : true,
59019         /**
59020          * @event celldblclick
59021          * Fires when a cell is double clicked
59022          * @param {Grid} this
59023          * @param {Number} rowIndex
59024          * @param {Number} columnIndex
59025          * @param {Roo.EventObject} e
59026          */
59027         "celldblclick" : true,
59028         /**
59029          * @event rowclick
59030          * Fires when a row is clicked
59031          * @param {Grid} this
59032          * @param {Number} rowIndex
59033          * @param {Roo.EventObject} e
59034          */
59035         "rowclick" : true,
59036         /**
59037          * @event rowdblclick
59038          * Fires when a row is double clicked
59039          * @param {Grid} this
59040          * @param {Number} rowIndex
59041          * @param {Roo.EventObject} e
59042          */
59043         "rowdblclick" : true,
59044         /**
59045          * @event headerclick
59046          * Fires when a header is clicked
59047          * @param {Grid} this
59048          * @param {Number} columnIndex
59049          * @param {Roo.EventObject} e
59050          */
59051         "headerclick" : true,
59052         /**
59053          * @event headerdblclick
59054          * Fires when a header cell is double clicked
59055          * @param {Grid} this
59056          * @param {Number} columnIndex
59057          * @param {Roo.EventObject} e
59058          */
59059         "headerdblclick" : true,
59060         /**
59061          * @event rowcontextmenu
59062          * Fires when a row is right clicked
59063          * @param {Grid} this
59064          * @param {Number} rowIndex
59065          * @param {Roo.EventObject} e
59066          */
59067         "rowcontextmenu" : true,
59068         /**
59069          * @event cellcontextmenu
59070          * Fires when a cell is right clicked
59071          * @param {Grid} this
59072          * @param {Number} rowIndex
59073          * @param {Number} cellIndex
59074          * @param {Roo.EventObject} e
59075          */
59076          "cellcontextmenu" : true,
59077         /**
59078          * @event headercontextmenu
59079          * Fires when a header is right clicked
59080          * @param {Grid} this
59081          * @param {Number} columnIndex
59082          * @param {Roo.EventObject} e
59083          */
59084         "headercontextmenu" : true,
59085         /**
59086          * @event bodyscroll
59087          * Fires when the body element is scrolled
59088          * @param {Number} scrollLeft
59089          * @param {Number} scrollTop
59090          */
59091         "bodyscroll" : true,
59092         /**
59093          * @event columnresize
59094          * Fires when the user resizes a column
59095          * @param {Number} columnIndex
59096          * @param {Number} newSize
59097          */
59098         "columnresize" : true,
59099         /**
59100          * @event columnmove
59101          * Fires when the user moves a column
59102          * @param {Number} oldIndex
59103          * @param {Number} newIndex
59104          */
59105         "columnmove" : true,
59106         /**
59107          * @event startdrag
59108          * Fires when row(s) start being dragged
59109          * @param {Grid} this
59110          * @param {Roo.GridDD} dd The drag drop object
59111          * @param {event} e The raw browser event
59112          */
59113         "startdrag" : true,
59114         /**
59115          * @event enddrag
59116          * Fires when a drag operation is complete
59117          * @param {Grid} this
59118          * @param {Roo.GridDD} dd The drag drop object
59119          * @param {event} e The raw browser event
59120          */
59121         "enddrag" : true,
59122         /**
59123          * @event dragdrop
59124          * Fires when dragged row(s) are dropped on a valid DD target
59125          * @param {Grid} this
59126          * @param {Roo.GridDD} dd The drag drop object
59127          * @param {String} targetId The target drag drop object
59128          * @param {event} e The raw browser event
59129          */
59130         "dragdrop" : true,
59131         /**
59132          * @event dragover
59133          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59134          * @param {Grid} this
59135          * @param {Roo.GridDD} dd The drag drop object
59136          * @param {String} targetId The target drag drop object
59137          * @param {event} e The raw browser event
59138          */
59139         "dragover" : true,
59140         /**
59141          * @event dragenter
59142          *  Fires when the dragged row(s) first cross another DD target while being dragged
59143          * @param {Grid} this
59144          * @param {Roo.GridDD} dd The drag drop object
59145          * @param {String} targetId The target drag drop object
59146          * @param {event} e The raw browser event
59147          */
59148         "dragenter" : true,
59149         /**
59150          * @event dragout
59151          * Fires when the dragged row(s) leave another DD target while being dragged
59152          * @param {Grid} this
59153          * @param {Roo.GridDD} dd The drag drop object
59154          * @param {String} targetId The target drag drop object
59155          * @param {event} e The raw browser event
59156          */
59157         "dragout" : true,
59158         /**
59159          * @event rowclass
59160          * Fires when a row is rendered, so you can change add a style to it.
59161          * @param {GridView} gridview   The grid view
59162          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59163          */
59164         'rowclass' : true,
59165
59166         /**
59167          * @event render
59168          * Fires when the grid is rendered
59169          * @param {Grid} grid
59170          */
59171         'render' : true,
59172             /**
59173              * @event select
59174              * Fires when a date is selected
59175              * @param {DatePicker} this
59176              * @param {Date} date The selected date
59177              */
59178         'select': true,
59179         /**
59180              * @event monthchange
59181              * Fires when the displayed month changes 
59182              * @param {DatePicker} this
59183              * @param {Date} date The selected month
59184              */
59185         'monthchange': true,
59186         /**
59187              * @event evententer
59188              * Fires when mouse over an event
59189              * @param {Calendar} this
59190              * @param {event} Event
59191              */
59192         'evententer': true,
59193         /**
59194              * @event eventleave
59195              * Fires when the mouse leaves an
59196              * @param {Calendar} this
59197              * @param {event}
59198              */
59199         'eventleave': true,
59200         /**
59201              * @event eventclick
59202              * Fires when the mouse click an
59203              * @param {Calendar} this
59204              * @param {event}
59205              */
59206         'eventclick': true,
59207         /**
59208              * @event eventrender
59209              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59210              * @param {Calendar} this
59211              * @param {data} data to be modified
59212              */
59213         'eventrender': true
59214         
59215     });
59216
59217     Roo.grid.Grid.superclass.constructor.call(this);
59218     this.on('render', function() {
59219         this.view.el.addClass('x-grid-cal'); 
59220         
59221         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59222
59223     },this);
59224     
59225     if (!Roo.grid.Calendar.style) {
59226         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59227             
59228             
59229             '.x-grid-cal .x-grid-col' :  {
59230                 height: 'auto !important',
59231                 'vertical-align': 'top'
59232             },
59233             '.x-grid-cal  .fc-event-hori' : {
59234                 height: '14px'
59235             }
59236              
59237             
59238         }, Roo.id());
59239     }
59240
59241     
59242     
59243 };
59244 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59245     /**
59246      * @cfg {Store} eventStore The store that loads events.
59247      */
59248     eventStore : 25,
59249
59250      
59251     activeDate : false,
59252     startDay : 0,
59253     autoWidth : true,
59254     monitorWindowResize : false,
59255
59256     
59257     resizeColumns : function() {
59258         var col = (this.view.el.getWidth() / 7) - 3;
59259         // loop through cols, and setWidth
59260         for(var i =0 ; i < 7 ; i++){
59261             this.cm.setColumnWidth(i, col);
59262         }
59263     },
59264      setDate :function(date) {
59265         
59266         Roo.log('setDate?');
59267         
59268         this.resizeColumns();
59269         var vd = this.activeDate;
59270         this.activeDate = date;
59271 //        if(vd && this.el){
59272 //            var t = date.getTime();
59273 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59274 //                Roo.log('using add remove');
59275 //                
59276 //                this.fireEvent('monthchange', this, date);
59277 //                
59278 //                this.cells.removeClass("fc-state-highlight");
59279 //                this.cells.each(function(c){
59280 //                   if(c.dateValue == t){
59281 //                       c.addClass("fc-state-highlight");
59282 //                       setTimeout(function(){
59283 //                            try{c.dom.firstChild.focus();}catch(e){}
59284 //                       }, 50);
59285 //                       return false;
59286 //                   }
59287 //                   return true;
59288 //                });
59289 //                return;
59290 //            }
59291 //        }
59292         
59293         var days = date.getDaysInMonth();
59294         
59295         var firstOfMonth = date.getFirstDateOfMonth();
59296         var startingPos = firstOfMonth.getDay()-this.startDay;
59297         
59298         if(startingPos < this.startDay){
59299             startingPos += 7;
59300         }
59301         
59302         var pm = date.add(Date.MONTH, -1);
59303         var prevStart = pm.getDaysInMonth()-startingPos;
59304 //        
59305         
59306         
59307         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59308         
59309         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59310         //this.cells.addClassOnOver('fc-state-hover');
59311         
59312         var cells = this.cells.elements;
59313         var textEls = this.textNodes;
59314         
59315         //Roo.each(cells, function(cell){
59316         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59317         //});
59318         
59319         days += startingPos;
59320
59321         // convert everything to numbers so it's fast
59322         var day = 86400000;
59323         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59324         //Roo.log(d);
59325         //Roo.log(pm);
59326         //Roo.log(prevStart);
59327         
59328         var today = new Date().clearTime().getTime();
59329         var sel = date.clearTime().getTime();
59330         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59331         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59332         var ddMatch = this.disabledDatesRE;
59333         var ddText = this.disabledDatesText;
59334         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59335         var ddaysText = this.disabledDaysText;
59336         var format = this.format;
59337         
59338         var setCellClass = function(cal, cell){
59339             
59340             //Roo.log('set Cell Class');
59341             cell.title = "";
59342             var t = d.getTime();
59343             
59344             //Roo.log(d);
59345             
59346             
59347             cell.dateValue = t;
59348             if(t == today){
59349                 cell.className += " fc-today";
59350                 cell.className += " fc-state-highlight";
59351                 cell.title = cal.todayText;
59352             }
59353             if(t == sel){
59354                 // disable highlight in other month..
59355                 cell.className += " fc-state-highlight";
59356                 
59357             }
59358             // disabling
59359             if(t < min) {
59360                 //cell.className = " fc-state-disabled";
59361                 cell.title = cal.minText;
59362                 return;
59363             }
59364             if(t > max) {
59365                 //cell.className = " fc-state-disabled";
59366                 cell.title = cal.maxText;
59367                 return;
59368             }
59369             if(ddays){
59370                 if(ddays.indexOf(d.getDay()) != -1){
59371                     // cell.title = ddaysText;
59372                    // cell.className = " fc-state-disabled";
59373                 }
59374             }
59375             if(ddMatch && format){
59376                 var fvalue = d.dateFormat(format);
59377                 if(ddMatch.test(fvalue)){
59378                     cell.title = ddText.replace("%0", fvalue);
59379                    cell.className = " fc-state-disabled";
59380                 }
59381             }
59382             
59383             if (!cell.initialClassName) {
59384                 cell.initialClassName = cell.dom.className;
59385             }
59386             
59387             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59388         };
59389
59390         var i = 0;
59391         
59392         for(; i < startingPos; i++) {
59393             cells[i].dayName =  (++prevStart);
59394             Roo.log(textEls[i]);
59395             d.setDate(d.getDate()+1);
59396             
59397             //cells[i].className = "fc-past fc-other-month";
59398             setCellClass(this, cells[i]);
59399         }
59400         
59401         var intDay = 0;
59402         
59403         for(; i < days; i++){
59404             intDay = i - startingPos + 1;
59405             cells[i].dayName =  (intDay);
59406             d.setDate(d.getDate()+1);
59407             
59408             cells[i].className = ''; // "x-date-active";
59409             setCellClass(this, cells[i]);
59410         }
59411         var extraDays = 0;
59412         
59413         for(; i < 42; i++) {
59414             //textEls[i].innerHTML = (++extraDays);
59415             
59416             d.setDate(d.getDate()+1);
59417             cells[i].dayName = (++extraDays);
59418             cells[i].className = "fc-future fc-other-month";
59419             setCellClass(this, cells[i]);
59420         }
59421         
59422         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59423         
59424         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59425         
59426         // this will cause all the cells to mis
59427         var rows= [];
59428         var i =0;
59429         for (var r = 0;r < 6;r++) {
59430             for (var c =0;c < 7;c++) {
59431                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59432             }    
59433         }
59434         
59435         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59436         for(i=0;i<cells.length;i++) {
59437             
59438             this.cells.elements[i].dayName = cells[i].dayName ;
59439             this.cells.elements[i].className = cells[i].className;
59440             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59441             this.cells.elements[i].title = cells[i].title ;
59442             this.cells.elements[i].dateValue = cells[i].dateValue ;
59443         }
59444         
59445         
59446         
59447         
59448         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59449         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59450         
59451         ////if(totalRows != 6){
59452             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59453            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59454        // }
59455         
59456         this.fireEvent('monthchange', this, date);
59457         
59458         
59459     },
59460  /**
59461      * Returns the grid's SelectionModel.
59462      * @return {SelectionModel}
59463      */
59464     getSelectionModel : function(){
59465         if(!this.selModel){
59466             this.selModel = new Roo.grid.CellSelectionModel();
59467         }
59468         return this.selModel;
59469     },
59470
59471     load: function() {
59472         this.eventStore.load()
59473         
59474         
59475         
59476     },
59477     
59478     findCell : function(dt) {
59479         dt = dt.clearTime().getTime();
59480         var ret = false;
59481         this.cells.each(function(c){
59482             //Roo.log("check " +c.dateValue + '?=' + dt);
59483             if(c.dateValue == dt){
59484                 ret = c;
59485                 return false;
59486             }
59487             return true;
59488         });
59489         
59490         return ret;
59491     },
59492     
59493     findCells : function(rec) {
59494         var s = rec.data.start_dt.clone().clearTime().getTime();
59495        // Roo.log(s);
59496         var e= rec.data.end_dt.clone().clearTime().getTime();
59497        // Roo.log(e);
59498         var ret = [];
59499         this.cells.each(function(c){
59500              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59501             
59502             if(c.dateValue > e){
59503                 return ;
59504             }
59505             if(c.dateValue < s){
59506                 return ;
59507             }
59508             ret.push(c);
59509         });
59510         
59511         return ret;    
59512     },
59513     
59514     findBestRow: function(cells)
59515     {
59516         var ret = 0;
59517         
59518         for (var i =0 ; i < cells.length;i++) {
59519             ret  = Math.max(cells[i].rows || 0,ret);
59520         }
59521         return ret;
59522         
59523     },
59524     
59525     
59526     addItem : function(rec)
59527     {
59528         // look for vertical location slot in
59529         var cells = this.findCells(rec);
59530         
59531         rec.row = this.findBestRow(cells);
59532         
59533         // work out the location.
59534         
59535         var crow = false;
59536         var rows = [];
59537         for(var i =0; i < cells.length; i++) {
59538             if (!crow) {
59539                 crow = {
59540                     start : cells[i],
59541                     end :  cells[i]
59542                 };
59543                 continue;
59544             }
59545             if (crow.start.getY() == cells[i].getY()) {
59546                 // on same row.
59547                 crow.end = cells[i];
59548                 continue;
59549             }
59550             // different row.
59551             rows.push(crow);
59552             crow = {
59553                 start: cells[i],
59554                 end : cells[i]
59555             };
59556             
59557         }
59558         
59559         rows.push(crow);
59560         rec.els = [];
59561         rec.rows = rows;
59562         rec.cells = cells;
59563         for (var i = 0; i < cells.length;i++) {
59564             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59565             
59566         }
59567         
59568         
59569     },
59570     
59571     clearEvents: function() {
59572         
59573         if (!this.eventStore.getCount()) {
59574             return;
59575         }
59576         // reset number of rows in cells.
59577         Roo.each(this.cells.elements, function(c){
59578             c.rows = 0;
59579         });
59580         
59581         this.eventStore.each(function(e) {
59582             this.clearEvent(e);
59583         },this);
59584         
59585     },
59586     
59587     clearEvent : function(ev)
59588     {
59589         if (ev.els) {
59590             Roo.each(ev.els, function(el) {
59591                 el.un('mouseenter' ,this.onEventEnter, this);
59592                 el.un('mouseleave' ,this.onEventLeave, this);
59593                 el.remove();
59594             },this);
59595             ev.els = [];
59596         }
59597     },
59598     
59599     
59600     renderEvent : function(ev,ctr) {
59601         if (!ctr) {
59602              ctr = this.view.el.select('.fc-event-container',true).first();
59603         }
59604         
59605          
59606         this.clearEvent(ev);
59607             //code
59608        
59609         
59610         
59611         ev.els = [];
59612         var cells = ev.cells;
59613         var rows = ev.rows;
59614         this.fireEvent('eventrender', this, ev);
59615         
59616         for(var i =0; i < rows.length; i++) {
59617             
59618             cls = '';
59619             if (i == 0) {
59620                 cls += ' fc-event-start';
59621             }
59622             if ((i+1) == rows.length) {
59623                 cls += ' fc-event-end';
59624             }
59625             
59626             //Roo.log(ev.data);
59627             // how many rows should it span..
59628             var cg = this.eventTmpl.append(ctr,Roo.apply({
59629                 fccls : cls
59630                 
59631             }, ev.data) , true);
59632             
59633             
59634             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59635             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59636             cg.on('click', this.onEventClick, this, ev);
59637             
59638             ev.els.push(cg);
59639             
59640             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59641             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59642             //Roo.log(cg);
59643              
59644             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59645             cg.setWidth(ebox.right - sbox.x -2);
59646         }
59647     },
59648     
59649     renderEvents: function()
59650     {   
59651         // first make sure there is enough space..
59652         
59653         if (!this.eventTmpl) {
59654             this.eventTmpl = new Roo.Template(
59655                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59656                     '<div class="fc-event-inner">' +
59657                         '<span class="fc-event-time">{time}</span>' +
59658                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59659                     '</div>' +
59660                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59661                 '</div>'
59662             );
59663                 
59664         }
59665                
59666         
59667         
59668         this.cells.each(function(c) {
59669             //Roo.log(c.select('.fc-day-content div',true).first());
59670             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59671         });
59672         
59673         var ctr = this.view.el.select('.fc-event-container',true).first();
59674         
59675         var cls;
59676         this.eventStore.each(function(ev){
59677             
59678             this.renderEvent(ev);
59679              
59680              
59681         }, this);
59682         this.view.layout();
59683         
59684     },
59685     
59686     onEventEnter: function (e, el,event,d) {
59687         this.fireEvent('evententer', this, el, event);
59688     },
59689     
59690     onEventLeave: function (e, el,event,d) {
59691         this.fireEvent('eventleave', this, el, event);
59692     },
59693     
59694     onEventClick: function (e, el,event,d) {
59695         this.fireEvent('eventclick', this, el, event);
59696     },
59697     
59698     onMonthChange: function () {
59699         this.store.load();
59700     },
59701     
59702     onLoad: function () {
59703         
59704         //Roo.log('calendar onload');
59705 //         
59706         if(this.eventStore.getCount() > 0){
59707             
59708            
59709             
59710             this.eventStore.each(function(d){
59711                 
59712                 
59713                 // FIXME..
59714                 var add =   d.data;
59715                 if (typeof(add.end_dt) == 'undefined')  {
59716                     Roo.log("Missing End time in calendar data: ");
59717                     Roo.log(d);
59718                     return;
59719                 }
59720                 if (typeof(add.start_dt) == 'undefined')  {
59721                     Roo.log("Missing Start time in calendar data: ");
59722                     Roo.log(d);
59723                     return;
59724                 }
59725                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59726                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59727                 add.id = add.id || d.id;
59728                 add.title = add.title || '??';
59729                 
59730                 this.addItem(d);
59731                 
59732              
59733             },this);
59734         }
59735         
59736         this.renderEvents();
59737     }
59738     
59739
59740 });
59741 /*
59742  grid : {
59743                 xtype: 'Grid',
59744                 xns: Roo.grid,
59745                 listeners : {
59746                     render : function ()
59747                     {
59748                         _this.grid = this;
59749                         
59750                         if (!this.view.el.hasClass('course-timesheet')) {
59751                             this.view.el.addClass('course-timesheet');
59752                         }
59753                         if (this.tsStyle) {
59754                             this.ds.load({});
59755                             return; 
59756                         }
59757                         Roo.log('width');
59758                         Roo.log(_this.grid.view.el.getWidth());
59759                         
59760                         
59761                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59762                             '.course-timesheet .x-grid-row' : {
59763                                 height: '80px'
59764                             },
59765                             '.x-grid-row td' : {
59766                                 'vertical-align' : 0
59767                             },
59768                             '.course-edit-link' : {
59769                                 'color' : 'blue',
59770                                 'text-overflow' : 'ellipsis',
59771                                 'overflow' : 'hidden',
59772                                 'white-space' : 'nowrap',
59773                                 'cursor' : 'pointer'
59774                             },
59775                             '.sub-link' : {
59776                                 'color' : 'green'
59777                             },
59778                             '.de-act-sup-link' : {
59779                                 'color' : 'purple',
59780                                 'text-decoration' : 'line-through'
59781                             },
59782                             '.de-act-link' : {
59783                                 'color' : 'red',
59784                                 'text-decoration' : 'line-through'
59785                             },
59786                             '.course-timesheet .course-highlight' : {
59787                                 'border-top-style': 'dashed !important',
59788                                 'border-bottom-bottom': 'dashed !important'
59789                             },
59790                             '.course-timesheet .course-item' : {
59791                                 'font-family'   : 'tahoma, arial, helvetica',
59792                                 'font-size'     : '11px',
59793                                 'overflow'      : 'hidden',
59794                                 'padding-left'  : '10px',
59795                                 'padding-right' : '10px',
59796                                 'padding-top' : '10px' 
59797                             }
59798                             
59799                         }, Roo.id());
59800                                 this.ds.load({});
59801                     }
59802                 },
59803                 autoWidth : true,
59804                 monitorWindowResize : false,
59805                 cellrenderer : function(v,x,r)
59806                 {
59807                     return v;
59808                 },
59809                 sm : {
59810                     xtype: 'CellSelectionModel',
59811                     xns: Roo.grid
59812                 },
59813                 dataSource : {
59814                     xtype: 'Store',
59815                     xns: Roo.data,
59816                     listeners : {
59817                         beforeload : function (_self, options)
59818                         {
59819                             options.params = options.params || {};
59820                             options.params._month = _this.monthField.getValue();
59821                             options.params.limit = 9999;
59822                             options.params['sort'] = 'when_dt';    
59823                             options.params['dir'] = 'ASC';    
59824                             this.proxy.loadResponse = this.loadResponse;
59825                             Roo.log("load?");
59826                             //this.addColumns();
59827                         },
59828                         load : function (_self, records, options)
59829                         {
59830                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59831                                 // if you click on the translation.. you can edit it...
59832                                 var el = Roo.get(this);
59833                                 var id = el.dom.getAttribute('data-id');
59834                                 var d = el.dom.getAttribute('data-date');
59835                                 var t = el.dom.getAttribute('data-time');
59836                                 //var id = this.child('span').dom.textContent;
59837                                 
59838                                 //Roo.log(this);
59839                                 Pman.Dialog.CourseCalendar.show({
59840                                     id : id,
59841                                     when_d : d,
59842                                     when_t : t,
59843                                     productitem_active : id ? 1 : 0
59844                                 }, function() {
59845                                     _this.grid.ds.load({});
59846                                 });
59847                            
59848                            });
59849                            
59850                            _this.panel.fireEvent('resize', [ '', '' ]);
59851                         }
59852                     },
59853                     loadResponse : function(o, success, response){
59854                             // this is overridden on before load..
59855                             
59856                             Roo.log("our code?");       
59857                             //Roo.log(success);
59858                             //Roo.log(response)
59859                             delete this.activeRequest;
59860                             if(!success){
59861                                 this.fireEvent("loadexception", this, o, response);
59862                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59863                                 return;
59864                             }
59865                             var result;
59866                             try {
59867                                 result = o.reader.read(response);
59868                             }catch(e){
59869                                 Roo.log("load exception?");
59870                                 this.fireEvent("loadexception", this, o, response, e);
59871                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59872                                 return;
59873                             }
59874                             Roo.log("ready...");        
59875                             // loop through result.records;
59876                             // and set this.tdate[date] = [] << array of records..
59877                             _this.tdata  = {};
59878                             Roo.each(result.records, function(r){
59879                                 //Roo.log(r.data);
59880                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59881                                     _this.tdata[r.data.when_dt.format('j')] = [];
59882                                 }
59883                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59884                             });
59885                             
59886                             //Roo.log(_this.tdata);
59887                             
59888                             result.records = [];
59889                             result.totalRecords = 6;
59890                     
59891                             // let's generate some duumy records for the rows.
59892                             //var st = _this.dateField.getValue();
59893                             
59894                             // work out monday..
59895                             //st = st.add(Date.DAY, -1 * st.format('w'));
59896                             
59897                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59898                             
59899                             var firstOfMonth = date.getFirstDayOfMonth();
59900                             var days = date.getDaysInMonth();
59901                             var d = 1;
59902                             var firstAdded = false;
59903                             for (var i = 0; i < result.totalRecords ; i++) {
59904                                 //var d= st.add(Date.DAY, i);
59905                                 var row = {};
59906                                 var added = 0;
59907                                 for(var w = 0 ; w < 7 ; w++){
59908                                     if(!firstAdded && firstOfMonth != w){
59909                                         continue;
59910                                     }
59911                                     if(d > days){
59912                                         continue;
59913                                     }
59914                                     firstAdded = true;
59915                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59916                                     row['weekday'+w] = String.format(
59917                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59918                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59919                                                     d,
59920                                                     date.format('Y-m-')+dd
59921                                                 );
59922                                     added++;
59923                                     if(typeof(_this.tdata[d]) != 'undefined'){
59924                                         Roo.each(_this.tdata[d], function(r){
59925                                             var is_sub = '';
59926                                             var deactive = '';
59927                                             var id = r.id;
59928                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59929                                             if(r.parent_id*1>0){
59930                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59931                                                 id = r.parent_id;
59932                                             }
59933                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59934                                                 deactive = 'de-act-link';
59935                                             }
59936                                             
59937                                             row['weekday'+w] += String.format(
59938                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59939                                                     id, //0
59940                                                     r.product_id_name, //1
59941                                                     r.when_dt.format('h:ia'), //2
59942                                                     is_sub, //3
59943                                                     deactive, //4
59944                                                     desc // 5
59945                                             );
59946                                         });
59947                                     }
59948                                     d++;
59949                                 }
59950                                 
59951                                 // only do this if something added..
59952                                 if(added > 0){ 
59953                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59954                                 }
59955                                 
59956                                 
59957                                 // push it twice. (second one with an hour..
59958                                 
59959                             }
59960                             //Roo.log(result);
59961                             this.fireEvent("load", this, o, o.request.arg);
59962                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59963                         },
59964                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59965                     proxy : {
59966                         xtype: 'HttpProxy',
59967                         xns: Roo.data,
59968                         method : 'GET',
59969                         url : baseURL + '/Roo/Shop_course.php'
59970                     },
59971                     reader : {
59972                         xtype: 'JsonReader',
59973                         xns: Roo.data,
59974                         id : 'id',
59975                         fields : [
59976                             {
59977                                 'name': 'id',
59978                                 'type': 'int'
59979                             },
59980                             {
59981                                 'name': 'when_dt',
59982                                 'type': 'string'
59983                             },
59984                             {
59985                                 'name': 'end_dt',
59986                                 'type': 'string'
59987                             },
59988                             {
59989                                 'name': 'parent_id',
59990                                 'type': 'int'
59991                             },
59992                             {
59993                                 'name': 'product_id',
59994                                 'type': 'int'
59995                             },
59996                             {
59997                                 'name': 'productitem_id',
59998                                 'type': 'int'
59999                             },
60000                             {
60001                                 'name': 'guid',
60002                                 'type': 'int'
60003                             }
60004                         ]
60005                     }
60006                 },
60007                 toolbar : {
60008                     xtype: 'Toolbar',
60009                     xns: Roo,
60010                     items : [
60011                         {
60012                             xtype: 'Button',
60013                             xns: Roo.Toolbar,
60014                             listeners : {
60015                                 click : function (_self, e)
60016                                 {
60017                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60018                                     sd.setMonth(sd.getMonth()-1);
60019                                     _this.monthField.setValue(sd.format('Y-m-d'));
60020                                     _this.grid.ds.load({});
60021                                 }
60022                             },
60023                             text : "Back"
60024                         },
60025                         {
60026                             xtype: 'Separator',
60027                             xns: Roo.Toolbar
60028                         },
60029                         {
60030                             xtype: 'MonthField',
60031                             xns: Roo.form,
60032                             listeners : {
60033                                 render : function (_self)
60034                                 {
60035                                     _this.monthField = _self;
60036                                    // _this.monthField.set  today
60037                                 },
60038                                 select : function (combo, date)
60039                                 {
60040                                     _this.grid.ds.load({});
60041                                 }
60042                             },
60043                             value : (function() { return new Date(); })()
60044                         },
60045                         {
60046                             xtype: 'Separator',
60047                             xns: Roo.Toolbar
60048                         },
60049                         {
60050                             xtype: 'TextItem',
60051                             xns: Roo.Toolbar,
60052                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60053                         },
60054                         {
60055                             xtype: 'Fill',
60056                             xns: Roo.Toolbar
60057                         },
60058                         {
60059                             xtype: 'Button',
60060                             xns: Roo.Toolbar,
60061                             listeners : {
60062                                 click : function (_self, e)
60063                                 {
60064                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60065                                     sd.setMonth(sd.getMonth()+1);
60066                                     _this.monthField.setValue(sd.format('Y-m-d'));
60067                                     _this.grid.ds.load({});
60068                                 }
60069                             },
60070                             text : "Next"
60071                         }
60072                     ]
60073                 },
60074                  
60075             }
60076         };
60077         
60078         *//*
60079  * Based on:
60080  * Ext JS Library 1.1.1
60081  * Copyright(c) 2006-2007, Ext JS, LLC.
60082  *
60083  * Originally Released Under LGPL - original licence link has changed is not relivant.
60084  *
60085  * Fork - LGPL
60086  * <script type="text/javascript">
60087  */
60088  
60089 /**
60090  * @class Roo.LoadMask
60091  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60092  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60093  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60094  * element's UpdateManager load indicator and will be destroyed after the initial load.
60095  * @constructor
60096  * Create a new LoadMask
60097  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60098  * @param {Object} config The config object
60099  */
60100 Roo.LoadMask = function(el, config){
60101     this.el = Roo.get(el);
60102     Roo.apply(this, config);
60103     if(this.store){
60104         this.store.on('beforeload', this.onBeforeLoad, this);
60105         this.store.on('load', this.onLoad, this);
60106         this.store.on('loadexception', this.onLoadException, this);
60107         this.removeMask = false;
60108     }else{
60109         var um = this.el.getUpdateManager();
60110         um.showLoadIndicator = false; // disable the default indicator
60111         um.on('beforeupdate', this.onBeforeLoad, this);
60112         um.on('update', this.onLoad, this);
60113         um.on('failure', this.onLoad, this);
60114         this.removeMask = true;
60115     }
60116 };
60117
60118 Roo.LoadMask.prototype = {
60119     /**
60120      * @cfg {Boolean} removeMask
60121      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60122      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60123      */
60124     /**
60125      * @cfg {String} msg
60126      * The text to display in a centered loading message box (defaults to 'Loading...')
60127      */
60128     msg : 'Loading...',
60129     /**
60130      * @cfg {String} msgCls
60131      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60132      */
60133     msgCls : 'x-mask-loading',
60134
60135     /**
60136      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60137      * @type Boolean
60138      */
60139     disabled: false,
60140
60141     /**
60142      * Disables the mask to prevent it from being displayed
60143      */
60144     disable : function(){
60145        this.disabled = true;
60146     },
60147
60148     /**
60149      * Enables the mask so that it can be displayed
60150      */
60151     enable : function(){
60152         this.disabled = false;
60153     },
60154     
60155     onLoadException : function()
60156     {
60157         Roo.log(arguments);
60158         
60159         if (typeof(arguments[3]) != 'undefined') {
60160             Roo.MessageBox.alert("Error loading",arguments[3]);
60161         } 
60162         /*
60163         try {
60164             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60165                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60166             }   
60167         } catch(e) {
60168             
60169         }
60170         */
60171     
60172         
60173         
60174         this.el.unmask(this.removeMask);
60175     },
60176     // private
60177     onLoad : function()
60178     {
60179         this.el.unmask(this.removeMask);
60180     },
60181
60182     // private
60183     onBeforeLoad : function(){
60184         if(!this.disabled){
60185             this.el.mask(this.msg, this.msgCls);
60186         }
60187     },
60188
60189     // private
60190     destroy : function(){
60191         if(this.store){
60192             this.store.un('beforeload', this.onBeforeLoad, this);
60193             this.store.un('load', this.onLoad, this);
60194             this.store.un('loadexception', this.onLoadException, this);
60195         }else{
60196             var um = this.el.getUpdateManager();
60197             um.un('beforeupdate', this.onBeforeLoad, this);
60198             um.un('update', this.onLoad, this);
60199             um.un('failure', this.onLoad, this);
60200         }
60201     }
60202 };/*
60203  * Based on:
60204  * Ext JS Library 1.1.1
60205  * Copyright(c) 2006-2007, Ext JS, LLC.
60206  *
60207  * Originally Released Under LGPL - original licence link has changed is not relivant.
60208  *
60209  * Fork - LGPL
60210  * <script type="text/javascript">
60211  */
60212
60213
60214 /**
60215  * @class Roo.XTemplate
60216  * @extends Roo.Template
60217  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60218 <pre><code>
60219 var t = new Roo.XTemplate(
60220         '&lt;select name="{name}"&gt;',
60221                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60222         '&lt;/select&gt;'
60223 );
60224  
60225 // then append, applying the master template values
60226  </code></pre>
60227  *
60228  * Supported features:
60229  *
60230  *  Tags:
60231
60232 <pre><code>
60233       {a_variable} - output encoded.
60234       {a_variable.format:("Y-m-d")} - call a method on the variable
60235       {a_variable:raw} - unencoded output
60236       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60237       {a_variable:this.method_on_template(...)} - call a method on the template object.
60238  
60239 </code></pre>
60240  *  The tpl tag:
60241 <pre><code>
60242         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60243         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60244         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60245         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60246   
60247         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60248         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60249 </code></pre>
60250  *      
60251  */
60252 Roo.XTemplate = function()
60253 {
60254     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60255     if (this.html) {
60256         this.compile();
60257     }
60258 };
60259
60260
60261 Roo.extend(Roo.XTemplate, Roo.Template, {
60262
60263     /**
60264      * The various sub templates
60265      */
60266     tpls : false,
60267     /**
60268      *
60269      * basic tag replacing syntax
60270      * WORD:WORD()
60271      *
60272      * // you can fake an object call by doing this
60273      *  x.t:(test,tesT) 
60274      * 
60275      */
60276     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60277
60278     /**
60279      * compile the template
60280      *
60281      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60282      *
60283      */
60284     compile: function()
60285     {
60286         var s = this.html;
60287      
60288         s = ['<tpl>', s, '</tpl>'].join('');
60289     
60290         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60291             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60292             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60293             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60294             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60295             m,
60296             id     = 0,
60297             tpls   = [];
60298     
60299         while(true == !!(m = s.match(re))){
60300             var forMatch   = m[0].match(nameRe),
60301                 ifMatch   = m[0].match(ifRe),
60302                 execMatch   = m[0].match(execRe),
60303                 namedMatch   = m[0].match(namedRe),
60304                 
60305                 exp  = null, 
60306                 fn   = null,
60307                 exec = null,
60308                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60309                 
60310             if (ifMatch) {
60311                 // if - puts fn into test..
60312                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60313                 if(exp){
60314                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60315                 }
60316             }
60317             
60318             if (execMatch) {
60319                 // exec - calls a function... returns empty if true is  returned.
60320                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60321                 if(exp){
60322                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60323                 }
60324             }
60325             
60326             
60327             if (name) {
60328                 // for = 
60329                 switch(name){
60330                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60331                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60332                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60333                 }
60334             }
60335             var uid = namedMatch ? namedMatch[1] : id;
60336             
60337             
60338             tpls.push({
60339                 id:     namedMatch ? namedMatch[1] : id,
60340                 target: name,
60341                 exec:   exec,
60342                 test:   fn,
60343                 body:   m[1] || ''
60344             });
60345             if (namedMatch) {
60346                 s = s.replace(m[0], '');
60347             } else { 
60348                 s = s.replace(m[0], '{xtpl'+ id + '}');
60349             }
60350             ++id;
60351         }
60352         this.tpls = [];
60353         for(var i = tpls.length-1; i >= 0; --i){
60354             this.compileTpl(tpls[i]);
60355             this.tpls[tpls[i].id] = tpls[i];
60356         }
60357         this.master = tpls[tpls.length-1];
60358         return this;
60359     },
60360     /**
60361      * same as applyTemplate, except it's done to one of the subTemplates
60362      * when using named templates, you can do:
60363      *
60364      * var str = pl.applySubTemplate('your-name', values);
60365      *
60366      * 
60367      * @param {Number} id of the template
60368      * @param {Object} values to apply to template
60369      * @param {Object} parent (normaly the instance of this object)
60370      */
60371     applySubTemplate : function(id, values, parent)
60372     {
60373         
60374         
60375         var t = this.tpls[id];
60376         
60377         
60378         try { 
60379             if(t.test && !t.test.call(this, values, parent)){
60380                 return '';
60381             }
60382         } catch(e) {
60383             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60384             Roo.log(e.toString());
60385             Roo.log(t.test);
60386             return ''
60387         }
60388         try { 
60389             
60390             if(t.exec && t.exec.call(this, values, parent)){
60391                 return '';
60392             }
60393         } catch(e) {
60394             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60395             Roo.log(e.toString());
60396             Roo.log(t.exec);
60397             return ''
60398         }
60399         try {
60400             var vs = t.target ? t.target.call(this, values, parent) : values;
60401             parent = t.target ? values : parent;
60402             if(t.target && vs instanceof Array){
60403                 var buf = [];
60404                 for(var i = 0, len = vs.length; i < len; i++){
60405                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60406                 }
60407                 return buf.join('');
60408             }
60409             return t.compiled.call(this, vs, parent);
60410         } catch (e) {
60411             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60412             Roo.log(e.toString());
60413             Roo.log(t.compiled);
60414             return '';
60415         }
60416     },
60417
60418     compileTpl : function(tpl)
60419     {
60420         var fm = Roo.util.Format;
60421         var useF = this.disableFormats !== true;
60422         var sep = Roo.isGecko ? "+" : ",";
60423         var undef = function(str) {
60424             Roo.log("Property not found :"  + str);
60425             return '';
60426         };
60427         
60428         var fn = function(m, name, format, args)
60429         {
60430             //Roo.log(arguments);
60431             args = args ? args.replace(/\\'/g,"'") : args;
60432             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60433             if (typeof(format) == 'undefined') {
60434                 format= 'htmlEncode';
60435             }
60436             if (format == 'raw' ) {
60437                 format = false;
60438             }
60439             
60440             if(name.substr(0, 4) == 'xtpl'){
60441                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60442             }
60443             
60444             // build an array of options to determine if value is undefined..
60445             
60446             // basically get 'xxxx.yyyy' then do
60447             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60448             //    (function () { Roo.log("Property not found"); return ''; })() :
60449             //    ......
60450             
60451             var udef_ar = [];
60452             var lookfor = '';
60453             Roo.each(name.split('.'), function(st) {
60454                 lookfor += (lookfor.length ? '.': '') + st;
60455                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60456             });
60457             
60458             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60459             
60460             
60461             if(format && useF){
60462                 
60463                 args = args ? ',' + args : "";
60464                  
60465                 if(format.substr(0, 5) != "this."){
60466                     format = "fm." + format + '(';
60467                 }else{
60468                     format = 'this.call("'+ format.substr(5) + '", ';
60469                     args = ", values";
60470                 }
60471                 
60472                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60473             }
60474              
60475             if (args.length) {
60476                 // called with xxyx.yuu:(test,test)
60477                 // change to ()
60478                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60479             }
60480             // raw.. - :raw modifier..
60481             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60482             
60483         };
60484         var body;
60485         // branched to use + in gecko and [].join() in others
60486         if(Roo.isGecko){
60487             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60488                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60489                     "';};};";
60490         }else{
60491             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60492             body.push(tpl.body.replace(/(\r\n|\n)/g,
60493                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60494             body.push("'].join('');};};");
60495             body = body.join('');
60496         }
60497         
60498         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60499        
60500         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60501         eval(body);
60502         
60503         return this;
60504     },
60505
60506     applyTemplate : function(values){
60507         return this.master.compiled.call(this, values, {});
60508         //var s = this.subs;
60509     },
60510
60511     apply : function(){
60512         return this.applyTemplate.apply(this, arguments);
60513     }
60514
60515  });
60516
60517 Roo.XTemplate.from = function(el){
60518     el = Roo.getDom(el);
60519     return new Roo.XTemplate(el.value || el.innerHTML);
60520 };