roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             console.log(s);
346             
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888 });
889
890 /**
891  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
892  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
893  * they are already different, the first value passed in is returned.  Note that this method returns the new value
894  * but does not change the current string.
895  * <pre><code>
896 // alternate sort directions
897 sort = sort.toggle('ASC', 'DESC');
898
899 // instead of conditional logic:
900 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
901 </code></pre>
902  * @param {String} value The value to compare to the current string
903  * @param {String} other The new value to use if the string already equals the first value passed in
904  * @return {String} The new value
905  */
906  
907 String.prototype.toggle = function(value, other){
908     return this == value ? other : value;
909 };/*
910  * Based on:
911  * Ext JS Library 1.1.1
912  * Copyright(c) 2006-2007, Ext JS, LLC.
913  *
914  * Originally Released Under LGPL - original licence link has changed is not relivant.
915  *
916  * Fork - LGPL
917  * <script type="text/javascript">
918  */
919
920  /**
921  * @class Number
922  */
923 Roo.applyIf(Number.prototype, {
924     /**
925      * Checks whether or not the current number is within a desired range.  If the number is already within the
926      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
927      * exceeded.  Note that this method returns the constrained value but does not change the current number.
928      * @param {Number} min The minimum number in the range
929      * @param {Number} max The maximum number in the range
930      * @return {Number} The constrained value if outside the range, otherwise the current value
931      */
932     constrain : function(min, max){
933         return Math.min(Math.max(this, min), max);
934     }
935 });/*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945  /**
946  * @class Array
947  */
948 Roo.applyIf(Array.prototype, {
949     /**
950      * 
951      * Checks whether or not the specified object exists in the array.
952      * @param {Object} o The object to check for
953      * @return {Number} The index of o in the array (or -1 if it is not found)
954      */
955     indexOf : function(o){
956        for (var i = 0, len = this.length; i < len; i++){
957               if(this[i] == o) { return i; }
958        }
959            return -1;
960     },
961
962     /**
963      * Removes the specified object from the array.  If the object is not found nothing happens.
964      * @param {Object} o The object to remove
965      */
966     remove : function(o){
967        var index = this.indexOf(o);
968        if(index != -1){
969            this.splice(index, 1);
970        }
971     },
972     /**
973      * Map (JS 1.6 compatibility)
974      * @param {Function} function  to call
975      */
976     map : function(fun )
977     {
978         var len = this.length >>> 0;
979         if (typeof fun != "function") {
980             throw new TypeError();
981         }
982         var res = new Array(len);
983         var thisp = arguments[1];
984         for (var i = 0; i < len; i++)
985         {
986             if (i in this) {
987                 res[i] = fun.call(thisp, this[i], i, this);
988             }
989         }
990
991         return res;
992     }
993     
994 });
995
996
997  
998 /*
999  * Based on:
1000  * Ext JS Library 1.1.1
1001  * Copyright(c) 2006-2007, Ext JS, LLC.
1002  *
1003  * Originally Released Under LGPL - original licence link has changed is not relivant.
1004  *
1005  * Fork - LGPL
1006  * <script type="text/javascript">
1007  */
1008
1009 /**
1010  * @class Date
1011  *
1012  * The date parsing and format syntax is a subset of
1013  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1014  * supported will provide results equivalent to their PHP versions.
1015  *
1016  * Following is the list of all currently supported formats:
1017  *<pre>
1018 Sample date:
1019 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1020
1021 Format  Output      Description
1022 ------  ----------  --------------------------------------------------------------
1023   d      10         Day of the month, 2 digits with leading zeros
1024   D      Wed        A textual representation of a day, three letters
1025   j      10         Day of the month without leading zeros
1026   l      Wednesday  A full textual representation of the day of the week
1027   S      th         English ordinal day of month suffix, 2 chars (use with j)
1028   w      3          Numeric representation of the day of the week
1029   z      9          The julian date, or day of the year (0-365)
1030   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1031   F      January    A full textual representation of the month
1032   m      01         Numeric representation of a month, with leading zeros
1033   M      Jan        Month name abbreviation, three letters
1034   n      1          Numeric representation of a month, without leading zeros
1035   t      31         Number of days in the given month
1036   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1037   Y      2007       A full numeric representation of a year, 4 digits
1038   y      07         A two digit representation of a year
1039   a      pm         Lowercase Ante meridiem and Post meridiem
1040   A      PM         Uppercase Ante meridiem and Post meridiem
1041   g      3          12-hour format of an hour without leading zeros
1042   G      15         24-hour format of an hour without leading zeros
1043   h      03         12-hour format of an hour with leading zeros
1044   H      15         24-hour format of an hour with leading zeros
1045   i      05         Minutes with leading zeros
1046   s      01         Seconds, with leading zeros
1047   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1048   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1049   T      CST        Timezone setting of the machine running the code
1050   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1051 </pre>
1052  *
1053  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1054  * <pre><code>
1055 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1056 document.write(dt.format('Y-m-d'));                         //2007-01-10
1057 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1058 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
1059  </code></pre>
1060  *
1061  * Here are some standard date/time patterns that you might find helpful.  They
1062  * are not part of the source of Date.js, but to use them you can simply copy this
1063  * block of code into any script that is included after Date.js and they will also become
1064  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1065  * <pre><code>
1066 Date.patterns = {
1067     ISO8601Long:"Y-m-d H:i:s",
1068     ISO8601Short:"Y-m-d",
1069     ShortDate: "n/j/Y",
1070     LongDate: "l, F d, Y",
1071     FullDateTime: "l, F d, Y g:i:s A",
1072     MonthDay: "F d",
1073     ShortTime: "g:i A",
1074     LongTime: "g:i:s A",
1075     SortableDateTime: "Y-m-d\\TH:i:s",
1076     UniversalSortableDateTime: "Y-m-d H:i:sO",
1077     YearMonth: "F, Y"
1078 };
1079 </code></pre>
1080  *
1081  * Example usage:
1082  * <pre><code>
1083 var dt = new Date();
1084 document.write(dt.format(Date.patterns.ShortDate));
1085  </code></pre>
1086  */
1087
1088 /*
1089  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1090  * They generate precompiled functions from date formats instead of parsing and
1091  * processing the pattern every time you format a date.  These functions are available
1092  * on every Date object (any javascript function).
1093  *
1094  * The original article and download are here:
1095  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1096  *
1097  */
1098  
1099  
1100  // was in core
1101 /**
1102  Returns the number of milliseconds between this date and date
1103  @param {Date} date (optional) Defaults to now
1104  @return {Number} The diff in milliseconds
1105  @member Date getElapsed
1106  */
1107 Date.prototype.getElapsed = function(date) {
1108         return Math.abs((date || new Date()).getTime()-this.getTime());
1109 };
1110 // was in date file..
1111
1112
1113 // private
1114 Date.parseFunctions = {count:0};
1115 // private
1116 Date.parseRegexes = [];
1117 // private
1118 Date.formatFunctions = {count:0};
1119
1120 // private
1121 Date.prototype.dateFormat = function(format) {
1122     if (Date.formatFunctions[format] == null) {
1123         Date.createNewFormat(format);
1124     }
1125     var func = Date.formatFunctions[format];
1126     return this[func]();
1127 };
1128
1129
1130 /**
1131  * Formats a date given the supplied format string
1132  * @param {String} format The format string
1133  * @return {String} The formatted date
1134  * @method
1135  */
1136 Date.prototype.format = Date.prototype.dateFormat;
1137
1138 // private
1139 Date.createNewFormat = function(format) {
1140     var funcName = "format" + Date.formatFunctions.count++;
1141     Date.formatFunctions[format] = funcName;
1142     var code = "Date.prototype." + funcName + " = function(){return ";
1143     var special = false;
1144     var ch = '';
1145     for (var i = 0; i < format.length; ++i) {
1146         ch = format.charAt(i);
1147         if (!special && ch == "\\") {
1148             special = true;
1149         }
1150         else if (special) {
1151             special = false;
1152             code += "'" + String.escape(ch) + "' + ";
1153         }
1154         else {
1155             code += Date.getFormatCode(ch);
1156         }
1157     }
1158     /** eval:var:zzzzzzzzzzzzz */
1159     eval(code.substring(0, code.length - 3) + ";}");
1160 };
1161
1162 // private
1163 Date.getFormatCode = function(character) {
1164     switch (character) {
1165     case "d":
1166         return "String.leftPad(this.getDate(), 2, '0') + ";
1167     case "D":
1168         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1169     case "j":
1170         return "this.getDate() + ";
1171     case "l":
1172         return "Date.dayNames[this.getDay()] + ";
1173     case "S":
1174         return "this.getSuffix() + ";
1175     case "w":
1176         return "this.getDay() + ";
1177     case "z":
1178         return "this.getDayOfYear() + ";
1179     case "W":
1180         return "this.getWeekOfYear() + ";
1181     case "F":
1182         return "Date.monthNames[this.getMonth()] + ";
1183     case "m":
1184         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1185     case "M":
1186         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1187     case "n":
1188         return "(this.getMonth() + 1) + ";
1189     case "t":
1190         return "this.getDaysInMonth() + ";
1191     case "L":
1192         return "(this.isLeapYear() ? 1 : 0) + ";
1193     case "Y":
1194         return "this.getFullYear() + ";
1195     case "y":
1196         return "('' + this.getFullYear()).substring(2, 4) + ";
1197     case "a":
1198         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1199     case "A":
1200         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1201     case "g":
1202         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1203     case "G":
1204         return "this.getHours() + ";
1205     case "h":
1206         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1207     case "H":
1208         return "String.leftPad(this.getHours(), 2, '0') + ";
1209     case "i":
1210         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1211     case "s":
1212         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1213     case "O":
1214         return "this.getGMTOffset() + ";
1215     case "P":
1216         return "this.getGMTColonOffset() + ";
1217     case "T":
1218         return "this.getTimezone() + ";
1219     case "Z":
1220         return "(this.getTimezoneOffset() * -60) + ";
1221     default:
1222         return "'" + String.escape(character) + "' + ";
1223     }
1224 };
1225
1226 /**
1227  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1228  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1229  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1230  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1231  * string or the parse operation will fail.
1232  * Example Usage:
1233 <pre><code>
1234 //dt = Fri May 25 2007 (current date)
1235 var dt = new Date();
1236
1237 //dt = Thu May 25 2006 (today's month/day in 2006)
1238 dt = Date.parseDate("2006", "Y");
1239
1240 //dt = Sun Jan 15 2006 (all date parts specified)
1241 dt = Date.parseDate("2006-1-15", "Y-m-d");
1242
1243 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1244 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1245 </code></pre>
1246  * @param {String} input The unparsed date as a string
1247  * @param {String} format The format the date is in
1248  * @return {Date} The parsed date
1249  * @static
1250  */
1251 Date.parseDate = function(input, format) {
1252     if (Date.parseFunctions[format] == null) {
1253         Date.createParser(format);
1254     }
1255     var func = Date.parseFunctions[format];
1256     return Date[func](input);
1257 };
1258 /**
1259  * @private
1260  */
1261
1262 Date.createParser = function(format) {
1263     var funcName = "parse" + Date.parseFunctions.count++;
1264     var regexNum = Date.parseRegexes.length;
1265     var currentGroup = 1;
1266     Date.parseFunctions[format] = funcName;
1267
1268     var code = "Date." + funcName + " = function(input){\n"
1269         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1270         + "var d = new Date();\n"
1271         + "y = d.getFullYear();\n"
1272         + "m = d.getMonth();\n"
1273         + "d = d.getDate();\n"
1274         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1275         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1276         + "if (results && results.length > 0) {";
1277     var regex = "";
1278
1279     var special = false;
1280     var ch = '';
1281     for (var i = 0; i < format.length; ++i) {
1282         ch = format.charAt(i);
1283         if (!special && ch == "\\") {
1284             special = true;
1285         }
1286         else if (special) {
1287             special = false;
1288             regex += String.escape(ch);
1289         }
1290         else {
1291             var obj = Date.formatCodeToRegex(ch, currentGroup);
1292             currentGroup += obj.g;
1293             regex += obj.s;
1294             if (obj.g && obj.c) {
1295                 code += obj.c;
1296             }
1297         }
1298     }
1299
1300     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1301         + "{v = new Date(y, m, d, h, i, s);}\n"
1302         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1303         + "{v = new Date(y, m, d, h, i);}\n"
1304         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1305         + "{v = new Date(y, m, d, h);}\n"
1306         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1307         + "{v = new Date(y, m, d);}\n"
1308         + "else if (y >= 0 && m >= 0)\n"
1309         + "{v = new Date(y, m);}\n"
1310         + "else if (y >= 0)\n"
1311         + "{v = new Date(y);}\n"
1312         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1313         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1314         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1315         + ";}";
1316
1317     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1318     /** eval:var:zzzzzzzzzzzzz */
1319     eval(code);
1320 };
1321
1322 // private
1323 Date.formatCodeToRegex = function(character, currentGroup) {
1324     switch (character) {
1325     case "D":
1326         return {g:0,
1327         c:null,
1328         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1329     case "j":
1330         return {g:1,
1331             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1332             s:"(\\d{1,2})"}; // day of month without leading zeroes
1333     case "d":
1334         return {g:1,
1335             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1336             s:"(\\d{2})"}; // day of month with leading zeroes
1337     case "l":
1338         return {g:0,
1339             c:null,
1340             s:"(?:" + Date.dayNames.join("|") + ")"};
1341     case "S":
1342         return {g:0,
1343             c:null,
1344             s:"(?:st|nd|rd|th)"};
1345     case "w":
1346         return {g:0,
1347             c:null,
1348             s:"\\d"};
1349     case "z":
1350         return {g:0,
1351             c:null,
1352             s:"(?:\\d{1,3})"};
1353     case "W":
1354         return {g:0,
1355             c:null,
1356             s:"(?:\\d{2})"};
1357     case "F":
1358         return {g:1,
1359             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1360             s:"(" + Date.monthNames.join("|") + ")"};
1361     case "M":
1362         return {g:1,
1363             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1364             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1365     case "n":
1366         return {g:1,
1367             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1368             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1369     case "m":
1370         return {g:1,
1371             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1372             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1373     case "t":
1374         return {g:0,
1375             c:null,
1376             s:"\\d{1,2}"};
1377     case "L":
1378         return {g:0,
1379             c:null,
1380             s:"(?:1|0)"};
1381     case "Y":
1382         return {g:1,
1383             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{4})"};
1385     case "y":
1386         return {g:1,
1387             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1388                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1389             s:"(\\d{1,2})"};
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 "A":
1397         return {g:1,
1398             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1399                 + "if (h == 12) { h = 0; }\n"
1400                 + "} else { if (h < 12) { h += 12; }}",
1401             s:"(AM|PM)"};
1402     case "g":
1403     case "G":
1404         return {g:1,
1405             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1407     case "h":
1408     case "H":
1409         return {g:1,
1410             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1412     case "i":
1413         return {g:1,
1414             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1415             s:"(\\d{2})"};
1416     case "s":
1417         return {g:1,
1418             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1419             s:"(\\d{2})"};
1420     case "O":
1421         return {g:1,
1422             c:[
1423                 "o = results[", currentGroup, "];\n",
1424                 "var sn = o.substring(0,1);\n", // get + / - sign
1425                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1426                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1427                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1428                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{2,4})"};
1431     
1432     
1433     case "P":
1434         return {g:1,
1435                 c:[
1436                    "o = results[", currentGroup, "];\n",
1437                    "var sn = o.substring(0,1);\n",
1438                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1439                    "var mn = o.substring(4,6) % 60;\n",
1440                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1441                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1442             ].join(""),
1443             s:"([+\-]\\d{4})"};
1444     case "T":
1445         return {g:0,
1446             c:null,
1447             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1448     case "Z":
1449         return {g:1,
1450             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1451                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1452             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1453     default:
1454         return {g:0,
1455             c:null,
1456             s:String.escape(character)};
1457     }
1458 };
1459
1460 /**
1461  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1462  * @return {String} The abbreviated timezone name (e.g. 'CST')
1463  */
1464 Date.prototype.getTimezone = function() {
1465     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1466 };
1467
1468 /**
1469  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1470  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1471  */
1472 Date.prototype.getGMTOffset = function() {
1473     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1474         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1475         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1476 };
1477
1478 /**
1479  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1480  * @return {String} 2-characters representing hours and 2-characters representing minutes
1481  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1482  */
1483 Date.prototype.getGMTColonOffset = function() {
1484         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1485                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1486                 + ":"
1487                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1488 }
1489
1490 /**
1491  * Get the numeric day number of the year, adjusted for leap year.
1492  * @return {Number} 0 through 364 (365 in leap years)
1493  */
1494 Date.prototype.getDayOfYear = function() {
1495     var num = 0;
1496     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1497     for (var i = 0; i < this.getMonth(); ++i) {
1498         num += Date.daysInMonth[i];
1499     }
1500     return num + this.getDate() - 1;
1501 };
1502
1503 /**
1504  * Get the string representation of the numeric week number of the year
1505  * (equivalent to the format specifier 'W').
1506  * @return {String} '00' through '52'
1507  */
1508 Date.prototype.getWeekOfYear = function() {
1509     // Skip to Thursday of this week
1510     var now = this.getDayOfYear() + (4 - this.getDay());
1511     // Find the first Thursday of the year
1512     var jan1 = new Date(this.getFullYear(), 0, 1);
1513     var then = (7 - jan1.getDay() + 4);
1514     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1515 };
1516
1517 /**
1518  * Whether or not the current date is in a leap year.
1519  * @return {Boolean} True if the current date is in a leap year, else false
1520  */
1521 Date.prototype.isLeapYear = function() {
1522     var year = this.getFullYear();
1523     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1524 };
1525
1526 /**
1527  * Get the first day of the current month, adjusted for leap year.  The returned value
1528  * is the numeric day index within the week (0-6) which can be used in conjunction with
1529  * the {@link #monthNames} array to retrieve the textual day name.
1530  * Example:
1531  *<pre><code>
1532 var dt = new Date('1/10/2007');
1533 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1534 </code></pre>
1535  * @return {Number} The day number (0-6)
1536  */
1537 Date.prototype.getFirstDayOfMonth = function() {
1538     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1539     return (day < 0) ? (day + 7) : day;
1540 };
1541
1542 /**
1543  * Get the last day of the current month, adjusted for leap year.  The returned value
1544  * is the numeric day index within the week (0-6) which can be used in conjunction with
1545  * the {@link #monthNames} array to retrieve the textual day name.
1546  * Example:
1547  *<pre><code>
1548 var dt = new Date('1/10/2007');
1549 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1550 </code></pre>
1551  * @return {Number} The day number (0-6)
1552  */
1553 Date.prototype.getLastDayOfMonth = function() {
1554     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1555     return (day < 0) ? (day + 7) : day;
1556 };
1557
1558
1559 /**
1560  * Get the first date of this date's month
1561  * @return {Date}
1562  */
1563 Date.prototype.getFirstDateOfMonth = function() {
1564     return new Date(this.getFullYear(), this.getMonth(), 1);
1565 };
1566
1567 /**
1568  * Get the last date of this date's month
1569  * @return {Date}
1570  */
1571 Date.prototype.getLastDateOfMonth = function() {
1572     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1573 };
1574 /**
1575  * Get the number of days in the current month, adjusted for leap year.
1576  * @return {Number} The number of days in the month
1577  */
1578 Date.prototype.getDaysInMonth = function() {
1579     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1580     return Date.daysInMonth[this.getMonth()];
1581 };
1582
1583 /**
1584  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1585  * @return {String} 'st, 'nd', 'rd' or 'th'
1586  */
1587 Date.prototype.getSuffix = function() {
1588     switch (this.getDate()) {
1589         case 1:
1590         case 21:
1591         case 31:
1592             return "st";
1593         case 2:
1594         case 22:
1595             return "nd";
1596         case 3:
1597         case 23:
1598             return "rd";
1599         default:
1600             return "th";
1601     }
1602 };
1603
1604 // private
1605 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1606
1607 /**
1608  * An array of textual month names.
1609  * Override these values for international dates, for example...
1610  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1611  * @type Array
1612  * @static
1613  */
1614 Date.monthNames =
1615    ["January",
1616     "February",
1617     "March",
1618     "April",
1619     "May",
1620     "June",
1621     "July",
1622     "August",
1623     "September",
1624     "October",
1625     "November",
1626     "December"];
1627
1628 /**
1629  * An array of textual day names.
1630  * Override these values for international dates, for example...
1631  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1632  * @type Array
1633  * @static
1634  */
1635 Date.dayNames =
1636    ["Sunday",
1637     "Monday",
1638     "Tuesday",
1639     "Wednesday",
1640     "Thursday",
1641     "Friday",
1642     "Saturday"];
1643
1644 // private
1645 Date.y2kYear = 50;
1646 // private
1647 Date.monthNumbers = {
1648     Jan:0,
1649     Feb:1,
1650     Mar:2,
1651     Apr:3,
1652     May:4,
1653     Jun:5,
1654     Jul:6,
1655     Aug:7,
1656     Sep:8,
1657     Oct:9,
1658     Nov:10,
1659     Dec:11};
1660
1661 /**
1662  * Creates and returns a new Date instance with the exact same date value as the called instance.
1663  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1664  * variable will also be changed.  When the intention is to create a new variable that will not
1665  * modify the original instance, you should create a clone.
1666  *
1667  * Example of correctly cloning a date:
1668  * <pre><code>
1669 //wrong way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig;
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 05 2006'!
1674
1675 //correct way:
1676 var orig = new Date('10/1/2006');
1677 var copy = orig.clone();
1678 copy.setDate(5);
1679 document.write(orig);  //returns 'Thu Oct 01 2006'
1680 </code></pre>
1681  * @return {Date} The new Date instance
1682  */
1683 Date.prototype.clone = function() {
1684         return new Date(this.getTime());
1685 };
1686
1687 /**
1688  * Clears any time information from this date
1689  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1690  @return {Date} this or the clone
1691  */
1692 Date.prototype.clearTime = function(clone){
1693     if(clone){
1694         return this.clone().clearTime();
1695     }
1696     this.setHours(0);
1697     this.setMinutes(0);
1698     this.setSeconds(0);
1699     this.setMilliseconds(0);
1700     return this;
1701 };
1702
1703 // private
1704 // safari setMonth is broken -- check that this is only donw once...
1705 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1706     Date.brokenSetMonth = Date.prototype.setMonth;
1707         Date.prototype.setMonth = function(num){
1708                 if(num <= -1){
1709                         var n = Math.ceil(-num);
1710                         var back_year = Math.ceil(n/12);
1711                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1712                         this.setFullYear(this.getFullYear() - back_year);
1713                         return Date.brokenSetMonth.call(this, month);
1714                 } else {
1715                         return Date.brokenSetMonth.apply(this, arguments);
1716                 }
1717         };
1718 }
1719
1720 /** Date interval constant 
1721 * @static 
1722 * @type String */
1723 Date.MILLI = "ms";
1724 /** Date interval constant 
1725 * @static 
1726 * @type String */
1727 Date.SECOND = "s";
1728 /** Date interval constant 
1729 * @static 
1730 * @type String */
1731 Date.MINUTE = "mi";
1732 /** Date interval constant 
1733 * @static 
1734 * @type String */
1735 Date.HOUR = "h";
1736 /** Date interval constant 
1737 * @static 
1738 * @type String */
1739 Date.DAY = "d";
1740 /** Date interval constant 
1741 * @static 
1742 * @type String */
1743 Date.MONTH = "mo";
1744 /** Date interval constant 
1745 * @static 
1746 * @type String */
1747 Date.YEAR = "y";
1748
1749 /**
1750  * Provides a convenient method of performing basic date arithmetic.  This method
1751  * does not modify the Date instance being called - it creates and returns
1752  * a new Date instance containing the resulting date value.
1753  *
1754  * Examples:
1755  * <pre><code>
1756 //Basic usage:
1757 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1758 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1759
1760 //Negative values will subtract correctly:
1761 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1762 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1763
1764 //You can even chain several calls together in one line!
1765 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1766 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1767  </code></pre>
1768  *
1769  * @param {String} interval   A valid date interval enum value
1770  * @param {Number} value      The amount to add to the current date
1771  * @return {Date} The new Date instance
1772  */
1773 Date.prototype.add = function(interval, value){
1774   var d = this.clone();
1775   if (!interval || value === 0) { return d; }
1776   switch(interval.toLowerCase()){
1777     case Date.MILLI:
1778       d.setMilliseconds(this.getMilliseconds() + value);
1779       break;
1780     case Date.SECOND:
1781       d.setSeconds(this.getSeconds() + value);
1782       break;
1783     case Date.MINUTE:
1784       d.setMinutes(this.getMinutes() + value);
1785       break;
1786     case Date.HOUR:
1787       d.setHours(this.getHours() + value);
1788       break;
1789     case Date.DAY:
1790       d.setDate(this.getDate() + value);
1791       break;
1792     case Date.MONTH:
1793       var day = this.getDate();
1794       if(day > 28){
1795           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1796       }
1797       d.setDate(day);
1798       d.setMonth(this.getMonth() + value);
1799       break;
1800     case Date.YEAR:
1801       d.setFullYear(this.getFullYear() + value);
1802       break;
1803   }
1804   return d;
1805 };
1806 /*
1807  * Based on:
1808  * Ext JS Library 1.1.1
1809  * Copyright(c) 2006-2007, Ext JS, LLC.
1810  *
1811  * Originally Released Under LGPL - original licence link has changed is not relivant.
1812  *
1813  * Fork - LGPL
1814  * <script type="text/javascript">
1815  */
1816
1817 /**
1818  * @class Roo.lib.Dom
1819  * @static
1820  * 
1821  * Dom utils (from YIU afaik)
1822  * 
1823  **/
1824 Roo.lib.Dom = {
1825     /**
1826      * Get the view width
1827      * @param {Boolean} full True will get the full document, otherwise it's the view width
1828      * @return {Number} The width
1829      */
1830      
1831     getViewWidth : function(full) {
1832         return full ? this.getDocumentWidth() : this.getViewportWidth();
1833     },
1834     /**
1835      * Get the view height
1836      * @param {Boolean} full True will get the full document, otherwise it's the view height
1837      * @return {Number} The height
1838      */
1839     getViewHeight : function(full) {
1840         return full ? this.getDocumentHeight() : this.getViewportHeight();
1841     },
1842
1843     getDocumentHeight: function() {
1844         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1845         return Math.max(scrollHeight, this.getViewportHeight());
1846     },
1847
1848     getDocumentWidth: function() {
1849         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1850         return Math.max(scrollWidth, this.getViewportWidth());
1851     },
1852
1853     getViewportHeight: function() {
1854         var height = self.innerHeight;
1855         var mode = document.compatMode;
1856
1857         if ((mode || Roo.isIE) && !Roo.isOpera) {
1858             height = (mode == "CSS1Compat") ?
1859                      document.documentElement.clientHeight :
1860                      document.body.clientHeight;
1861         }
1862
1863         return height;
1864     },
1865
1866     getViewportWidth: function() {
1867         var width = self.innerWidth;
1868         var mode = document.compatMode;
1869
1870         if (mode || Roo.isIE) {
1871             width = (mode == "CSS1Compat") ?
1872                     document.documentElement.clientWidth :
1873                     document.body.clientWidth;
1874         }
1875         return width;
1876     },
1877
1878     isAncestor : function(p, c) {
1879         p = Roo.getDom(p);
1880         c = Roo.getDom(c);
1881         if (!p || !c) {
1882             return false;
1883         }
1884
1885         if (p.contains && !Roo.isSafari) {
1886             return p.contains(c);
1887         } else if (p.compareDocumentPosition) {
1888             return !!(p.compareDocumentPosition(c) & 16);
1889         } else {
1890             var parent = c.parentNode;
1891             while (parent) {
1892                 if (parent == p) {
1893                     return true;
1894                 }
1895                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1896                     return false;
1897                 }
1898                 parent = parent.parentNode;
1899             }
1900             return false;
1901         }
1902     },
1903
1904     getRegion : function(el) {
1905         return Roo.lib.Region.getRegion(el);
1906     },
1907
1908     getY : function(el) {
1909         return this.getXY(el)[1];
1910     },
1911
1912     getX : function(el) {
1913         return this.getXY(el)[0];
1914     },
1915
1916     getXY : function(el) {
1917         var p, pe, b, scroll, bd = document.body;
1918         el = Roo.getDom(el);
1919         var fly = Roo.lib.AnimBase.fly;
1920         if (el.getBoundingClientRect) {
1921             b = el.getBoundingClientRect();
1922             scroll = fly(document).getScroll();
1923             return [b.left + scroll.left, b.top + scroll.top];
1924         }
1925         var x = 0, y = 0;
1926
1927         p = el;
1928
1929         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1930
1931         while (p) {
1932
1933             x += p.offsetLeft;
1934             y += p.offsetTop;
1935
1936             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1937                 hasAbsolute = true;
1938             }
1939
1940             if (Roo.isGecko) {
1941                 pe = fly(p);
1942
1943                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1944                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1945
1946
1947                 x += bl;
1948                 y += bt;
1949
1950
1951                 if (p != el && pe.getStyle('overflow') != 'visible') {
1952                     x += bl;
1953                     y += bt;
1954                 }
1955             }
1956             p = p.offsetParent;
1957         }
1958
1959         if (Roo.isSafari && hasAbsolute) {
1960             x -= bd.offsetLeft;
1961             y -= bd.offsetTop;
1962         }
1963
1964         if (Roo.isGecko && !hasAbsolute) {
1965             var dbd = fly(bd);
1966             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1967             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1968         }
1969
1970         p = el.parentNode;
1971         while (p && p != bd) {
1972             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1973                 x -= p.scrollLeft;
1974                 y -= p.scrollTop;
1975             }
1976             p = p.parentNode;
1977         }
1978         return [x, y];
1979     },
1980  
1981   
1982
1983
1984     setXY : function(el, xy) {
1985         el = Roo.fly(el, '_setXY');
1986         el.position();
1987         var pts = el.translatePoints(xy);
1988         if (xy[0] !== false) {
1989             el.dom.style.left = pts.left + "px";
1990         }
1991         if (xy[1] !== false) {
1992             el.dom.style.top = pts.top + "px";
1993         }
1994     },
1995
1996     setX : function(el, x) {
1997         this.setXY(el, [x, false]);
1998     },
1999
2000     setY : function(el, y) {
2001         this.setXY(el, [false, y]);
2002     }
2003 };
2004 /*
2005  * Portions of this file are based on pieces of Yahoo User Interface Library
2006  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2007  * YUI licensed under the BSD License:
2008  * http://developer.yahoo.net/yui/license.txt
2009  * <script type="text/javascript">
2010  *
2011  */
2012
2013 Roo.lib.Event = function() {
2014     var loadComplete = false;
2015     var listeners = [];
2016     var unloadListeners = [];
2017     var retryCount = 0;
2018     var onAvailStack = [];
2019     var counter = 0;
2020     var lastError = null;
2021
2022     return {
2023         POLL_RETRYS: 200,
2024         POLL_INTERVAL: 20,
2025         EL: 0,
2026         TYPE: 1,
2027         FN: 2,
2028         WFN: 3,
2029         OBJ: 3,
2030         ADJ_SCOPE: 4,
2031         _interval: null,
2032
2033         startInterval: function() {
2034             if (!this._interval) {
2035                 var self = this;
2036                 var callback = function() {
2037                     self._tryPreloadAttach();
2038                 };
2039                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2040
2041             }
2042         },
2043
2044         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2045             onAvailStack.push({ id:         p_id,
2046                 fn:         p_fn,
2047                 obj:        p_obj,
2048                 override:   p_override,
2049                 checkReady: false    });
2050
2051             retryCount = this.POLL_RETRYS;
2052             this.startInterval();
2053         },
2054
2055
2056         addListener: function(el, eventName, fn) {
2057             el = Roo.getDom(el);
2058             if (!el || !fn) {
2059                 return false;
2060             }
2061
2062             if ("unload" == eventName) {
2063                 unloadListeners[unloadListeners.length] =
2064                 [el, eventName, fn];
2065                 return true;
2066             }
2067
2068             var wrappedFn = function(e) {
2069                 return fn(Roo.lib.Event.getEvent(e));
2070             };
2071
2072             var li = [el, eventName, fn, wrappedFn];
2073
2074             var index = listeners.length;
2075             listeners[index] = li;
2076
2077             this.doAdd(el, eventName, wrappedFn, false);
2078             return true;
2079
2080         },
2081
2082
2083         removeListener: function(el, eventName, fn) {
2084             var i, len;
2085
2086             el = Roo.getDom(el);
2087
2088             if(!fn) {
2089                 return this.purgeElement(el, false, eventName);
2090             }
2091
2092
2093             if ("unload" == eventName) {
2094
2095                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2096                     var li = unloadListeners[i];
2097                     if (li &&
2098                         li[0] == el &&
2099                         li[1] == eventName &&
2100                         li[2] == fn) {
2101                         unloadListeners.splice(i, 1);
2102                         return true;
2103                     }
2104                 }
2105
2106                 return false;
2107             }
2108
2109             var cacheItem = null;
2110
2111
2112             var index = arguments[3];
2113
2114             if ("undefined" == typeof index) {
2115                 index = this._getCacheIndex(el, eventName, fn);
2116             }
2117
2118             if (index >= 0) {
2119                 cacheItem = listeners[index];
2120             }
2121
2122             if (!el || !cacheItem) {
2123                 return false;
2124             }
2125
2126             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2127
2128             delete listeners[index][this.WFN];
2129             delete listeners[index][this.FN];
2130             listeners.splice(index, 1);
2131
2132             return true;
2133
2134         },
2135
2136
2137         getTarget: function(ev, resolveTextNode) {
2138             ev = ev.browserEvent || ev;
2139             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2140             var t = ev.target || ev.srcElement;
2141             return this.resolveTextNode(t);
2142         },
2143
2144
2145         resolveTextNode: function(node) {
2146             if (Roo.isSafari && node && 3 == node.nodeType) {
2147                 return node.parentNode;
2148             } else {
2149                 return node;
2150             }
2151         },
2152
2153
2154         getPageX: function(ev) {
2155             ev = ev.browserEvent || ev;
2156             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2157             var x = ev.pageX;
2158             if (!x && 0 !== x) {
2159                 x = ev.clientX || 0;
2160
2161                 if (Roo.isIE) {
2162                     x += this.getScroll()[1];
2163                 }
2164             }
2165
2166             return x;
2167         },
2168
2169
2170         getPageY: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2173             var y = ev.pageY;
2174             if (!y && 0 !== y) {
2175                 y = ev.clientY || 0;
2176
2177                 if (Roo.isIE) {
2178                     y += this.getScroll()[0];
2179                 }
2180             }
2181
2182
2183             return y;
2184         },
2185
2186
2187         getXY: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2190             return [this.getPageX(ev), this.getPageY(ev)];
2191         },
2192
2193
2194         getRelatedTarget: function(ev) {
2195             ev = ev.browserEvent || ev;
2196             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2197             var t = ev.relatedTarget;
2198             if (!t) {
2199                 if (ev.type == "mouseout") {
2200                     t = ev.toElement;
2201                 } else if (ev.type == "mouseover") {
2202                     t = ev.fromElement;
2203                 }
2204             }
2205
2206             return this.resolveTextNode(t);
2207         },
2208
2209
2210         getTime: function(ev) {
2211             ev = ev.browserEvent || ev;
2212             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2213             if (!ev.time) {
2214                 var t = new Date().getTime();
2215                 try {
2216                     ev.time = t;
2217                 } catch(ex) {
2218                     this.lastError = ex;
2219                     return t;
2220                 }
2221             }
2222
2223             return ev.time;
2224         },
2225
2226
2227         stopEvent: function(ev) {
2228             this.stopPropagation(ev);
2229             this.preventDefault(ev);
2230         },
2231
2232
2233         stopPropagation: function(ev) {
2234             ev = ev.browserEvent || ev;
2235             if (ev.stopPropagation) {
2236                 ev.stopPropagation();
2237             } else {
2238                 ev.cancelBubble = true;
2239             }
2240         },
2241
2242
2243         preventDefault: function(ev) {
2244             ev = ev.browserEvent || ev;
2245             if(ev.preventDefault) {
2246                 ev.preventDefault();
2247             } else {
2248                 ev.returnValue = false;
2249             }
2250         },
2251
2252
2253         getEvent: function(e) {
2254             var ev = e || window.event;
2255             if (!ev) {
2256                 var c = this.getEvent.caller;
2257                 while (c) {
2258                     ev = c.arguments[0];
2259                     if (ev && Event == ev.constructor) {
2260                         break;
2261                     }
2262                     c = c.caller;
2263                 }
2264             }
2265             return ev;
2266         },
2267
2268
2269         getCharCode: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             return ev.charCode || ev.keyCode || 0;
2272         },
2273
2274
2275         _getCacheIndex: function(el, eventName, fn) {
2276             for (var i = 0,len = listeners.length; i < len; ++i) {
2277                 var li = listeners[i];
2278                 if (li &&
2279                     li[this.FN] == fn &&
2280                     li[this.EL] == el &&
2281                     li[this.TYPE] == eventName) {
2282                     return i;
2283                 }
2284             }
2285
2286             return -1;
2287         },
2288
2289
2290         elCache: {},
2291
2292
2293         getEl: function(id) {
2294             return document.getElementById(id);
2295         },
2296
2297
2298         clearCache: function() {
2299         },
2300
2301
2302         _load: function(e) {
2303             loadComplete = true;
2304             var EU = Roo.lib.Event;
2305
2306
2307             if (Roo.isIE) {
2308                 EU.doRemove(window, "load", EU._load);
2309             }
2310         },
2311
2312
2313         _tryPreloadAttach: function() {
2314
2315             if (this.locked) {
2316                 return false;
2317             }
2318
2319             this.locked = true;
2320
2321
2322             var tryAgain = !loadComplete;
2323             if (!tryAgain) {
2324                 tryAgain = (retryCount > 0);
2325             }
2326
2327
2328             var notAvail = [];
2329             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2330                 var item = onAvailStack[i];
2331                 if (item) {
2332                     var el = this.getEl(item.id);
2333
2334                     if (el) {
2335                         if (!item.checkReady ||
2336                             loadComplete ||
2337                             el.nextSibling ||
2338                             (document && document.body)) {
2339
2340                             var scope = el;
2341                             if (item.override) {
2342                                 if (item.override === true) {
2343                                     scope = item.obj;
2344                                 } else {
2345                                     scope = item.override;
2346                                 }
2347                             }
2348                             item.fn.call(scope, item.obj);
2349                             onAvailStack[i] = null;
2350                         }
2351                     } else {
2352                         notAvail.push(item);
2353                     }
2354                 }
2355             }
2356
2357             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2358
2359             if (tryAgain) {
2360
2361                 this.startInterval();
2362             } else {
2363                 clearInterval(this._interval);
2364                 this._interval = null;
2365             }
2366
2367             this.locked = false;
2368
2369             return true;
2370
2371         },
2372
2373
2374         purgeElement: function(el, recurse, eventName) {
2375             var elListeners = this.getListeners(el, eventName);
2376             if (elListeners) {
2377                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2378                     var l = elListeners[i];
2379                     this.removeListener(el, l.type, l.fn);
2380                 }
2381             }
2382
2383             if (recurse && el && el.childNodes) {
2384                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2385                     this.purgeElement(el.childNodes[i], recurse, eventName);
2386                 }
2387             }
2388         },
2389
2390
2391         getListeners: function(el, eventName) {
2392             var results = [], searchLists;
2393             if (!eventName) {
2394                 searchLists = [listeners, unloadListeners];
2395             } else if (eventName == "unload") {
2396                 searchLists = [unloadListeners];
2397             } else {
2398                 searchLists = [listeners];
2399             }
2400
2401             for (var j = 0; j < searchLists.length; ++j) {
2402                 var searchList = searchLists[j];
2403                 if (searchList && searchList.length > 0) {
2404                     for (var i = 0,len = searchList.length; i < len; ++i) {
2405                         var l = searchList[i];
2406                         if (l && l[this.EL] === el &&
2407                             (!eventName || eventName === l[this.TYPE])) {
2408                             results.push({
2409                                 type:   l[this.TYPE],
2410                                 fn:     l[this.FN],
2411                                 obj:    l[this.OBJ],
2412                                 adjust: l[this.ADJ_SCOPE],
2413                                 index:  i
2414                             });
2415                         }
2416                     }
2417                 }
2418             }
2419
2420             return (results.length) ? results : null;
2421         },
2422
2423
2424         _unload: function(e) {
2425
2426             var EU = Roo.lib.Event, i, j, l, len, index;
2427
2428             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2429                 l = unloadListeners[i];
2430                 if (l) {
2431                     var scope = window;
2432                     if (l[EU.ADJ_SCOPE]) {
2433                         if (l[EU.ADJ_SCOPE] === true) {
2434                             scope = l[EU.OBJ];
2435                         } else {
2436                             scope = l[EU.ADJ_SCOPE];
2437                         }
2438                     }
2439                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2440                     unloadListeners[i] = null;
2441                     l = null;
2442                     scope = null;
2443                 }
2444             }
2445
2446             unloadListeners = null;
2447
2448             if (listeners && listeners.length > 0) {
2449                 j = listeners.length;
2450                 while (j) {
2451                     index = j - 1;
2452                     l = listeners[index];
2453                     if (l) {
2454                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2455                                 l[EU.FN], index);
2456                     }
2457                     j = j - 1;
2458                 }
2459                 l = null;
2460
2461                 EU.clearCache();
2462             }
2463
2464             EU.doRemove(window, "unload", EU._unload);
2465
2466         },
2467
2468
2469         getScroll: function() {
2470             var dd = document.documentElement, db = document.body;
2471             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2472                 return [dd.scrollTop, dd.scrollLeft];
2473             } else if (db) {
2474                 return [db.scrollTop, db.scrollLeft];
2475             } else {
2476                 return [0, 0];
2477             }
2478         },
2479
2480
2481         doAdd: function () {
2482             if (window.addEventListener) {
2483                 return function(el, eventName, fn, capture) {
2484                     el.addEventListener(eventName, fn, (capture));
2485                 };
2486             } else if (window.attachEvent) {
2487                 return function(el, eventName, fn, capture) {
2488                     el.attachEvent("on" + eventName, fn);
2489                 };
2490             } else {
2491                 return function() {
2492                 };
2493             }
2494         }(),
2495
2496
2497         doRemove: function() {
2498             if (window.removeEventListener) {
2499                 return function (el, eventName, fn, capture) {
2500                     el.removeEventListener(eventName, fn, (capture));
2501                 };
2502             } else if (window.detachEvent) {
2503                 return function (el, eventName, fn) {
2504                     el.detachEvent("on" + eventName, fn);
2505                 };
2506             } else {
2507                 return function() {
2508                 };
2509             }
2510         }()
2511     };
2512     
2513 }();
2514 (function() {     
2515    
2516     var E = Roo.lib.Event;
2517     E.on = E.addListener;
2518     E.un = E.removeListener;
2519
2520     if (document && document.body) {
2521         E._load();
2522     } else {
2523         E.doAdd(window, "load", E._load);
2524     }
2525     E.doAdd(window, "unload", E._unload);
2526     E._tryPreloadAttach();
2527 })();
2528
2529 /*
2530  * Portions of this file are based on pieces of Yahoo User Interface Library
2531  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2532  * YUI licensed under the BSD License:
2533  * http://developer.yahoo.net/yui/license.txt
2534  * <script type="text/javascript">
2535  *
2536  */
2537
2538 (function() {
2539     /**
2540      * @class Roo.lib.Ajax
2541      *
2542      */
2543     Roo.lib.Ajax = {
2544         /**
2545          * @static 
2546          */
2547         request : function(method, uri, cb, data, options) {
2548             if(options){
2549                 var hs = options.headers;
2550                 if(hs){
2551                     for(var h in hs){
2552                         if(hs.hasOwnProperty(h)){
2553                             this.initHeader(h, hs[h], false);
2554                         }
2555                     }
2556                 }
2557                 if(options.xmlData){
2558                     this.initHeader('Content-Type', 'text/xml', false);
2559                     method = 'POST';
2560                     data = options.xmlData;
2561                 }
2562             }
2563
2564             return this.asyncRequest(method, uri, cb, data);
2565         },
2566
2567         serializeForm : function(form) {
2568             if(typeof form == 'string') {
2569                 form = (document.getElementById(form) || document.forms[form]);
2570             }
2571
2572             var el, name, val, disabled, data = '', hasSubmit = false;
2573             for (var i = 0; i < form.elements.length; i++) {
2574                 el = form.elements[i];
2575                 disabled = form.elements[i].disabled;
2576                 name = form.elements[i].name;
2577                 val = form.elements[i].value;
2578
2579                 if (!disabled && name){
2580                     switch (el.type)
2581                             {
2582                         case 'select-one':
2583                         case 'select-multiple':
2584                             for (var j = 0; j < el.options.length; j++) {
2585                                 if (el.options[j].selected) {
2586                                     if (Roo.isIE) {
2587                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2588                                     }
2589                                     else {
2590                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2591                                     }
2592                                 }
2593                             }
2594                             break;
2595                         case 'radio':
2596                         case 'checkbox':
2597                             if (el.checked) {
2598                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2599                             }
2600                             break;
2601                         case 'file':
2602
2603                         case undefined:
2604
2605                         case 'reset':
2606
2607                         case 'button':
2608
2609                             break;
2610                         case 'submit':
2611                             if(hasSubmit == false) {
2612                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2613                                 hasSubmit = true;
2614                             }
2615                             break;
2616                         default:
2617                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2618                             break;
2619                     }
2620                 }
2621             }
2622             data = data.substr(0, data.length - 1);
2623             return data;
2624         },
2625
2626         headers:{},
2627
2628         hasHeaders:false,
2629
2630         useDefaultHeader:true,
2631
2632         defaultPostHeader:'application/x-www-form-urlencoded',
2633
2634         useDefaultXhrHeader:true,
2635
2636         defaultXhrHeader:'XMLHttpRequest',
2637
2638         hasDefaultHeaders:true,
2639
2640         defaultHeaders:{},
2641
2642         poll:{},
2643
2644         timeout:{},
2645
2646         pollInterval:50,
2647
2648         transactionId:0,
2649
2650         setProgId:function(id)
2651         {
2652             this.activeX.unshift(id);
2653         },
2654
2655         setDefaultPostHeader:function(b)
2656         {
2657             this.useDefaultHeader = b;
2658         },
2659
2660         setDefaultXhrHeader:function(b)
2661         {
2662             this.useDefaultXhrHeader = b;
2663         },
2664
2665         setPollingInterval:function(i)
2666         {
2667             if (typeof i == 'number' && isFinite(i)) {
2668                 this.pollInterval = i;
2669             }
2670         },
2671
2672         createXhrObject:function(transactionId)
2673         {
2674             var obj,http;
2675             try
2676             {
2677
2678                 http = new XMLHttpRequest();
2679
2680                 obj = { conn:http, tId:transactionId };
2681             }
2682             catch(e)
2683             {
2684                 for (var i = 0; i < this.activeX.length; ++i) {
2685                     try
2686                     {
2687
2688                         http = new ActiveXObject(this.activeX[i]);
2689
2690                         obj = { conn:http, tId:transactionId };
2691                         break;
2692                     }
2693                     catch(e) {
2694                     }
2695                 }
2696             }
2697             finally
2698             {
2699                 return obj;
2700             }
2701         },
2702
2703         getConnectionObject:function()
2704         {
2705             var o;
2706             var tId = this.transactionId;
2707
2708             try
2709             {
2710                 o = this.createXhrObject(tId);
2711                 if (o) {
2712                     this.transactionId++;
2713                 }
2714             }
2715             catch(e) {
2716             }
2717             finally
2718             {
2719                 return o;
2720             }
2721         },
2722
2723         asyncRequest:function(method, uri, callback, postData)
2724         {
2725             var o = this.getConnectionObject();
2726
2727             if (!o) {
2728                 return null;
2729             }
2730             else {
2731                 o.conn.open(method, uri, true);
2732
2733                 if (this.useDefaultXhrHeader) {
2734                     if (!this.defaultHeaders['X-Requested-With']) {
2735                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2736                     }
2737                 }
2738
2739                 if(postData && this.useDefaultHeader){
2740                     this.initHeader('Content-Type', this.defaultPostHeader);
2741                 }
2742
2743                  if (this.hasDefaultHeaders || this.hasHeaders) {
2744                     this.setHeader(o);
2745                 }
2746
2747                 this.handleReadyState(o, callback);
2748                 o.conn.send(postData || null);
2749
2750                 return o;
2751             }
2752         },
2753
2754         handleReadyState:function(o, callback)
2755         {
2756             var oConn = this;
2757
2758             if (callback && callback.timeout) {
2759                 
2760                 this.timeout[o.tId] = window.setTimeout(function() {
2761                     oConn.abort(o, callback, true);
2762                 }, callback.timeout);
2763             }
2764
2765             this.poll[o.tId] = window.setInterval(
2766                     function() {
2767                         if (o.conn && o.conn.readyState == 4) {
2768                             window.clearInterval(oConn.poll[o.tId]);
2769                             delete oConn.poll[o.tId];
2770
2771                             if(callback && callback.timeout) {
2772                                 window.clearTimeout(oConn.timeout[o.tId]);
2773                                 delete oConn.timeout[o.tId];
2774                             }
2775
2776                             oConn.handleTransactionResponse(o, callback);
2777                         }
2778                     }
2779                     , this.pollInterval);
2780         },
2781
2782         handleTransactionResponse:function(o, callback, isAbort)
2783         {
2784
2785             if (!callback) {
2786                 this.releaseObject(o);
2787                 return;
2788             }
2789
2790             var httpStatus, responseObject;
2791
2792             try
2793             {
2794                 if (o.conn.status !== undefined && o.conn.status != 0) {
2795                     httpStatus = o.conn.status;
2796                 }
2797                 else {
2798                     httpStatus = 13030;
2799                 }
2800             }
2801             catch(e) {
2802
2803
2804                 httpStatus = 13030;
2805             }
2806
2807             if (httpStatus >= 200 && httpStatus < 300) {
2808                 responseObject = this.createResponseObject(o, callback.argument);
2809                 if (callback.success) {
2810                     if (!callback.scope) {
2811                         callback.success(responseObject);
2812                     }
2813                     else {
2814
2815
2816                         callback.success.apply(callback.scope, [responseObject]);
2817                     }
2818                 }
2819             }
2820             else {
2821                 switch (httpStatus) {
2822
2823                     case 12002:
2824                     case 12029:
2825                     case 12030:
2826                     case 12031:
2827                     case 12152:
2828                     case 13030:
2829                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2830                         if (callback.failure) {
2831                             if (!callback.scope) {
2832                                 callback.failure(responseObject);
2833                             }
2834                             else {
2835                                 callback.failure.apply(callback.scope, [responseObject]);
2836                             }
2837                         }
2838                         break;
2839                     default:
2840                         responseObject = this.createResponseObject(o, callback.argument);
2841                         if (callback.failure) {
2842                             if (!callback.scope) {
2843                                 callback.failure(responseObject);
2844                             }
2845                             else {
2846                                 callback.failure.apply(callback.scope, [responseObject]);
2847                             }
2848                         }
2849                 }
2850             }
2851
2852             this.releaseObject(o);
2853             responseObject = null;
2854         },
2855
2856         createResponseObject:function(o, callbackArg)
2857         {
2858             var obj = {};
2859             var headerObj = {};
2860
2861             try
2862             {
2863                 var headerStr = o.conn.getAllResponseHeaders();
2864                 var header = headerStr.split('\n');
2865                 for (var i = 0; i < header.length; i++) {
2866                     var delimitPos = header[i].indexOf(':');
2867                     if (delimitPos != -1) {
2868                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2869                     }
2870                 }
2871             }
2872             catch(e) {
2873             }
2874
2875             obj.tId = o.tId;
2876             obj.status = o.conn.status;
2877             obj.statusText = o.conn.statusText;
2878             obj.getResponseHeader = headerObj;
2879             obj.getAllResponseHeaders = headerStr;
2880             obj.responseText = o.conn.responseText;
2881             obj.responseXML = o.conn.responseXML;
2882
2883             if (typeof callbackArg !== undefined) {
2884                 obj.argument = callbackArg;
2885             }
2886
2887             return obj;
2888         },
2889
2890         createExceptionObject:function(tId, callbackArg, isAbort)
2891         {
2892             var COMM_CODE = 0;
2893             var COMM_ERROR = 'communication failure';
2894             var ABORT_CODE = -1;
2895             var ABORT_ERROR = 'transaction aborted';
2896
2897             var obj = {};
2898
2899             obj.tId = tId;
2900             if (isAbort) {
2901                 obj.status = ABORT_CODE;
2902                 obj.statusText = ABORT_ERROR;
2903             }
2904             else {
2905                 obj.status = COMM_CODE;
2906                 obj.statusText = COMM_ERROR;
2907             }
2908
2909             if (callbackArg) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         initHeader:function(label, value, isDefault)
2917         {
2918             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2919
2920             if (headerObj[label] === undefined) {
2921                 headerObj[label] = value;
2922             }
2923             else {
2924
2925
2926                 headerObj[label] = value + "," + headerObj[label];
2927             }
2928
2929             if (isDefault) {
2930                 this.hasDefaultHeaders = true;
2931             }
2932             else {
2933                 this.hasHeaders = true;
2934             }
2935         },
2936
2937
2938         setHeader:function(o)
2939         {
2940             if (this.hasDefaultHeaders) {
2941                 for (var prop in this.defaultHeaders) {
2942                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2943                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2944                     }
2945                 }
2946             }
2947
2948             if (this.hasHeaders) {
2949                 for (var prop in this.headers) {
2950                     if (this.headers.hasOwnProperty(prop)) {
2951                         o.conn.setRequestHeader(prop, this.headers[prop]);
2952                     }
2953                 }
2954                 this.headers = {};
2955                 this.hasHeaders = false;
2956             }
2957         },
2958
2959         resetDefaultHeaders:function() {
2960             delete this.defaultHeaders;
2961             this.defaultHeaders = {};
2962             this.hasDefaultHeaders = false;
2963         },
2964
2965         abort:function(o, callback, isTimeout)
2966         {
2967             if(this.isCallInProgress(o)) {
2968                 o.conn.abort();
2969                 window.clearInterval(this.poll[o.tId]);
2970                 delete this.poll[o.tId];
2971                 if (isTimeout) {
2972                     delete this.timeout[o.tId];
2973                 }
2974
2975                 this.handleTransactionResponse(o, callback, true);
2976
2977                 return true;
2978             }
2979             else {
2980                 return false;
2981             }
2982         },
2983
2984
2985         isCallInProgress:function(o)
2986         {
2987             if (o && o.conn) {
2988                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2989             }
2990             else {
2991
2992                 return false;
2993             }
2994         },
2995
2996
2997         releaseObject:function(o)
2998         {
2999
3000             o.conn = null;
3001
3002             o = null;
3003         },
3004
3005         activeX:[
3006         'MSXML2.XMLHTTP.3.0',
3007         'MSXML2.XMLHTTP',
3008         'Microsoft.XMLHTTP'
3009         ]
3010
3011
3012     };
3013 })();/*
3014  * Portions of this file are based on pieces of Yahoo User Interface Library
3015  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3016  * YUI licensed under the BSD License:
3017  * http://developer.yahoo.net/yui/license.txt
3018  * <script type="text/javascript">
3019  *
3020  */
3021
3022 Roo.lib.Region = function(t, r, b, l) {
3023     this.top = t;
3024     this[1] = t;
3025     this.right = r;
3026     this.bottom = b;
3027     this.left = l;
3028     this[0] = l;
3029 };
3030
3031
3032 Roo.lib.Region.prototype = {
3033     contains : function(region) {
3034         return ( region.left >= this.left &&
3035                  region.right <= this.right &&
3036                  region.top >= this.top &&
3037                  region.bottom <= this.bottom    );
3038
3039     },
3040
3041     getArea : function() {
3042         return ( (this.bottom - this.top) * (this.right - this.left) );
3043     },
3044
3045     intersect : function(region) {
3046         var t = Math.max(this.top, region.top);
3047         var r = Math.min(this.right, region.right);
3048         var b = Math.min(this.bottom, region.bottom);
3049         var l = Math.max(this.left, region.left);
3050
3051         if (b >= t && r >= l) {
3052             return new Roo.lib.Region(t, r, b, l);
3053         } else {
3054             return null;
3055         }
3056     },
3057     union : function(region) {
3058         var t = Math.min(this.top, region.top);
3059         var r = Math.max(this.right, region.right);
3060         var b = Math.max(this.bottom, region.bottom);
3061         var l = Math.min(this.left, region.left);
3062
3063         return new Roo.lib.Region(t, r, b, l);
3064     },
3065
3066     adjust : function(t, l, b, r) {
3067         this.top += t;
3068         this.left += l;
3069         this.right += r;
3070         this.bottom += b;
3071         return this;
3072     }
3073 };
3074
3075 Roo.lib.Region.getRegion = function(el) {
3076     var p = Roo.lib.Dom.getXY(el);
3077
3078     var t = p[1];
3079     var r = p[0] + el.offsetWidth;
3080     var b = p[1] + el.offsetHeight;
3081     var l = p[0];
3082
3083     return new Roo.lib.Region(t, r, b, l);
3084 };
3085 /*
3086  * Portions of this file are based on pieces of Yahoo User Interface Library
3087  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3088  * YUI licensed under the BSD License:
3089  * http://developer.yahoo.net/yui/license.txt
3090  * <script type="text/javascript">
3091  *
3092  */
3093 //@@dep Roo.lib.Region
3094
3095
3096 Roo.lib.Point = function(x, y) {
3097     if (x instanceof Array) {
3098         y = x[1];
3099         x = x[0];
3100     }
3101     this.x = this.right = this.left = this[0] = x;
3102     this.y = this.top = this.bottom = this[1] = y;
3103 };
3104
3105 Roo.lib.Point.prototype = new Roo.lib.Region();
3106 /*
3107  * Portions of this file are based on pieces of Yahoo User Interface Library
3108  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3109  * YUI licensed under the BSD License:
3110  * http://developer.yahoo.net/yui/license.txt
3111  * <script type="text/javascript">
3112  *
3113  */
3114  
3115 (function() {   
3116
3117     Roo.lib.Anim = {
3118         scroll : function(el, args, duration, easing, cb, scope) {
3119             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3120         },
3121
3122         motion : function(el, args, duration, easing, cb, scope) {
3123             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3124         },
3125
3126         color : function(el, args, duration, easing, cb, scope) {
3127             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3128         },
3129
3130         run : function(el, args, duration, easing, cb, scope, type) {
3131             type = type || Roo.lib.AnimBase;
3132             if (typeof easing == "string") {
3133                 easing = Roo.lib.Easing[easing];
3134             }
3135             var anim = new type(el, args, duration, easing);
3136             anim.animateX(function() {
3137                 Roo.callback(cb, scope);
3138             });
3139             return anim;
3140         }
3141     };
3142 })();/*
3143  * Portions of this file are based on pieces of Yahoo User Interface Library
3144  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3145  * YUI licensed under the BSD License:
3146  * http://developer.yahoo.net/yui/license.txt
3147  * <script type="text/javascript">
3148  *
3149  */
3150
3151 (function() {    
3152     var libFlyweight;
3153     
3154     function fly(el) {
3155         if (!libFlyweight) {
3156             libFlyweight = new Roo.Element.Flyweight();
3157         }
3158         libFlyweight.dom = el;
3159         return libFlyweight;
3160     }
3161
3162     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3163     
3164    
3165     
3166     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3167         if (el) {
3168             this.init(el, attributes, duration, method);
3169         }
3170     };
3171
3172     Roo.lib.AnimBase.fly = fly;
3173     
3174     
3175     
3176     Roo.lib.AnimBase.prototype = {
3177
3178         toString: function() {
3179             var el = this.getEl();
3180             var id = el.id || el.tagName;
3181             return ("Anim " + id);
3182         },
3183
3184         patterns: {
3185             noNegatives:        /width|height|opacity|padding/i,
3186             offsetAttribute:  /^((width|height)|(top|left))$/,
3187             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3188             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3189         },
3190
3191
3192         doMethod: function(attr, start, end) {
3193             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3194         },
3195
3196
3197         setAttribute: function(attr, val, unit) {
3198             if (this.patterns.noNegatives.test(attr)) {
3199                 val = (val > 0) ? val : 0;
3200             }
3201
3202             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3203         },
3204
3205
3206         getAttribute: function(attr) {
3207             var el = this.getEl();
3208             var val = fly(el).getStyle(attr);
3209
3210             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3211                 return parseFloat(val);
3212             }
3213
3214             var a = this.patterns.offsetAttribute.exec(attr) || [];
3215             var pos = !!( a[3] );
3216             var box = !!( a[2] );
3217
3218
3219             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3220                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3221             } else {
3222                 val = 0;
3223             }
3224
3225             return val;
3226         },
3227
3228
3229         getDefaultUnit: function(attr) {
3230             if (this.patterns.defaultUnit.test(attr)) {
3231                 return 'px';
3232             }
3233
3234             return '';
3235         },
3236
3237         animateX : function(callback, scope) {
3238             var f = function() {
3239                 this.onComplete.removeListener(f);
3240                 if (typeof callback == "function") {
3241                     callback.call(scope || this, this);
3242                 }
3243             };
3244             this.onComplete.addListener(f, this);
3245             this.animate();
3246         },
3247
3248
3249         setRuntimeAttribute: function(attr) {
3250             var start;
3251             var end;
3252             var attributes = this.attributes;
3253
3254             this.runtimeAttributes[attr] = {};
3255
3256             var isset = function(prop) {
3257                 return (typeof prop !== 'undefined');
3258             };
3259
3260             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3261                 return false;
3262             }
3263
3264             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3265
3266
3267             if (isset(attributes[attr]['to'])) {
3268                 end = attributes[attr]['to'];
3269             } else if (isset(attributes[attr]['by'])) {
3270                 if (start.constructor == Array) {
3271                     end = [];
3272                     for (var i = 0, len = start.length; i < len; ++i) {
3273                         end[i] = start[i] + attributes[attr]['by'][i];
3274                     }
3275                 } else {
3276                     end = start + attributes[attr]['by'];
3277                 }
3278             }
3279
3280             this.runtimeAttributes[attr].start = start;
3281             this.runtimeAttributes[attr].end = end;
3282
3283
3284             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3285         },
3286
3287
3288         init: function(el, attributes, duration, method) {
3289
3290             var isAnimated = false;
3291
3292
3293             var startTime = null;
3294
3295
3296             var actualFrames = 0;
3297
3298
3299             el = Roo.getDom(el);
3300
3301
3302             this.attributes = attributes || {};
3303
3304
3305             this.duration = duration || 1;
3306
3307
3308             this.method = method || Roo.lib.Easing.easeNone;
3309
3310
3311             this.useSeconds = true;
3312
3313
3314             this.currentFrame = 0;
3315
3316
3317             this.totalFrames = Roo.lib.AnimMgr.fps;
3318
3319
3320             this.getEl = function() {
3321                 return el;
3322             };
3323
3324
3325             this.isAnimated = function() {
3326                 return isAnimated;
3327             };
3328
3329
3330             this.getStartTime = function() {
3331                 return startTime;
3332             };
3333
3334             this.runtimeAttributes = {};
3335
3336
3337             this.animate = function() {
3338                 if (this.isAnimated()) {
3339                     return false;
3340                 }
3341
3342                 this.currentFrame = 0;
3343
3344                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3345
3346                 Roo.lib.AnimMgr.registerElement(this);
3347             };
3348
3349
3350             this.stop = function(finish) {
3351                 if (finish) {
3352                     this.currentFrame = this.totalFrames;
3353                     this._onTween.fire();
3354                 }
3355                 Roo.lib.AnimMgr.stop(this);
3356             };
3357
3358             var onStart = function() {
3359                 this.onStart.fire();
3360
3361                 this.runtimeAttributes = {};
3362                 for (var attr in this.attributes) {
3363                     this.setRuntimeAttribute(attr);
3364                 }
3365
3366                 isAnimated = true;
3367                 actualFrames = 0;
3368                 startTime = new Date();
3369             };
3370
3371
3372             var onTween = function() {
3373                 var data = {
3374                     duration: new Date() - this.getStartTime(),
3375                     currentFrame: this.currentFrame
3376                 };
3377
3378                 data.toString = function() {
3379                     return (
3380                             'duration: ' + data.duration +
3381                             ', currentFrame: ' + data.currentFrame
3382                             );
3383                 };
3384
3385                 this.onTween.fire(data);
3386
3387                 var runtimeAttributes = this.runtimeAttributes;
3388
3389                 for (var attr in runtimeAttributes) {
3390                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3391                 }
3392
3393                 actualFrames += 1;
3394             };
3395
3396             var onComplete = function() {
3397                 var actual_duration = (new Date() - startTime) / 1000 ;
3398
3399                 var data = {
3400                     duration: actual_duration,
3401                     frames: actualFrames,
3402                     fps: actualFrames / actual_duration
3403                 };
3404
3405                 data.toString = function() {
3406                     return (
3407                             'duration: ' + data.duration +
3408                             ', frames: ' + data.frames +
3409                             ', fps: ' + data.fps
3410                             );
3411                 };
3412
3413                 isAnimated = false;
3414                 actualFrames = 0;
3415                 this.onComplete.fire(data);
3416             };
3417
3418
3419             this._onStart = new Roo.util.Event(this);
3420             this.onStart = new Roo.util.Event(this);
3421             this.onTween = new Roo.util.Event(this);
3422             this._onTween = new Roo.util.Event(this);
3423             this.onComplete = new Roo.util.Event(this);
3424             this._onComplete = new Roo.util.Event(this);
3425             this._onStart.addListener(onStart);
3426             this._onTween.addListener(onTween);
3427             this._onComplete.addListener(onComplete);
3428         }
3429     };
3430 })();
3431 /*
3432  * Portions of this file are based on pieces of Yahoo User Interface Library
3433  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3434  * YUI licensed under the BSD License:
3435  * http://developer.yahoo.net/yui/license.txt
3436  * <script type="text/javascript">
3437  *
3438  */
3439
3440 Roo.lib.AnimMgr = new function() {
3441
3442     var thread = null;
3443
3444
3445     var queue = [];
3446
3447
3448     var tweenCount = 0;
3449
3450
3451     this.fps = 1000;
3452
3453
3454     this.delay = 1;
3455
3456
3457     this.registerElement = function(tween) {
3458         queue[queue.length] = tween;
3459         tweenCount += 1;
3460         tween._onStart.fire();
3461         this.start();
3462     };
3463
3464
3465     this.unRegister = function(tween, index) {
3466         tween._onComplete.fire();
3467         index = index || getIndex(tween);
3468         if (index != -1) {
3469             queue.splice(index, 1);
3470         }
3471
3472         tweenCount -= 1;
3473         if (tweenCount <= 0) {
3474             this.stop();
3475         }
3476     };
3477
3478
3479     this.start = function() {
3480         if (thread === null) {
3481             thread = setInterval(this.run, this.delay);
3482         }
3483     };
3484
3485
3486     this.stop = function(tween) {
3487         if (!tween) {
3488             clearInterval(thread);
3489
3490             for (var i = 0, len = queue.length; i < len; ++i) {
3491                 if (queue[0].isAnimated()) {
3492                     this.unRegister(queue[0], 0);
3493                 }
3494             }
3495
3496             queue = [];
3497             thread = null;
3498             tweenCount = 0;
3499         }
3500         else {
3501             this.unRegister(tween);
3502         }
3503     };
3504
3505
3506     this.run = function() {
3507         for (var i = 0, len = queue.length; i < len; ++i) {
3508             var tween = queue[i];
3509             if (!tween || !tween.isAnimated()) {
3510                 continue;
3511             }
3512
3513             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3514             {
3515                 tween.currentFrame += 1;
3516
3517                 if (tween.useSeconds) {
3518                     correctFrame(tween);
3519                 }
3520                 tween._onTween.fire();
3521             }
3522             else {
3523                 Roo.lib.AnimMgr.stop(tween, i);
3524             }
3525         }
3526     };
3527
3528     var getIndex = function(anim) {
3529         for (var i = 0, len = queue.length; i < len; ++i) {
3530             if (queue[i] == anim) {
3531                 return i;
3532             }
3533         }
3534         return -1;
3535     };
3536
3537
3538     var correctFrame = function(tween) {
3539         var frames = tween.totalFrames;
3540         var frame = tween.currentFrame;
3541         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3542         var elapsed = (new Date() - tween.getStartTime());
3543         var tweak = 0;
3544
3545         if (elapsed < tween.duration * 1000) {
3546             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3547         } else {
3548             tweak = frames - (frame + 1);
3549         }
3550         if (tweak > 0 && isFinite(tweak)) {
3551             if (tween.currentFrame + tweak >= frames) {
3552                 tweak = frames - (frame + 1);
3553             }
3554
3555             tween.currentFrame += tweak;
3556         }
3557     };
3558 };
3559
3560     /*
3561  * Portions of this file are based on pieces of Yahoo User Interface Library
3562  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3563  * YUI licensed under the BSD License:
3564  * http://developer.yahoo.net/yui/license.txt
3565  * <script type="text/javascript">
3566  *
3567  */
3568 Roo.lib.Bezier = new function() {
3569
3570         this.getPosition = function(points, t) {
3571             var n = points.length;
3572             var tmp = [];
3573
3574             for (var i = 0; i < n; ++i) {
3575                 tmp[i] = [points[i][0], points[i][1]];
3576             }
3577
3578             for (var j = 1; j < n; ++j) {
3579                 for (i = 0; i < n - j; ++i) {
3580                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3581                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3582                 }
3583             }
3584
3585             return [ tmp[0][0], tmp[0][1] ];
3586
3587         };
3588     };/*
3589  * Portions of this file are based on pieces of Yahoo User Interface Library
3590  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3591  * YUI licensed under the BSD License:
3592  * http://developer.yahoo.net/yui/license.txt
3593  * <script type="text/javascript">
3594  *
3595  */
3596 (function() {
3597
3598     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3599         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3600     };
3601
3602     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3603
3604     var fly = Roo.lib.AnimBase.fly;
3605     var Y = Roo.lib;
3606     var superclass = Y.ColorAnim.superclass;
3607     var proto = Y.ColorAnim.prototype;
3608
3609     proto.toString = function() {
3610         var el = this.getEl();
3611         var id = el.id || el.tagName;
3612         return ("ColorAnim " + id);
3613     };
3614
3615     proto.patterns.color = /color$/i;
3616     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3617     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3618     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3619     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3620
3621
3622     proto.parseColor = function(s) {
3623         if (s.length == 3) {
3624             return s;
3625         }
3626
3627         var c = this.patterns.hex.exec(s);
3628         if (c && c.length == 4) {
3629             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3630         }
3631
3632         c = this.patterns.rgb.exec(s);
3633         if (c && c.length == 4) {
3634             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3635         }
3636
3637         c = this.patterns.hex3.exec(s);
3638         if (c && c.length == 4) {
3639             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3640         }
3641
3642         return null;
3643     };
3644     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3645     proto.getAttribute = function(attr) {
3646         var el = this.getEl();
3647         if (this.patterns.color.test(attr)) {
3648             var val = fly(el).getStyle(attr);
3649
3650             if (this.patterns.transparent.test(val)) {
3651                 var parent = el.parentNode;
3652                 val = fly(parent).getStyle(attr);
3653
3654                 while (parent && this.patterns.transparent.test(val)) {
3655                     parent = parent.parentNode;
3656                     val = fly(parent).getStyle(attr);
3657                     if (parent.tagName.toUpperCase() == 'HTML') {
3658                         val = '#fff';
3659                     }
3660                 }
3661             }
3662         } else {
3663             val = superclass.getAttribute.call(this, attr);
3664         }
3665
3666         return val;
3667     };
3668     proto.getAttribute = function(attr) {
3669         var el = this.getEl();
3670         if (this.patterns.color.test(attr)) {
3671             var val = fly(el).getStyle(attr);
3672
3673             if (this.patterns.transparent.test(val)) {
3674                 var parent = el.parentNode;
3675                 val = fly(parent).getStyle(attr);
3676
3677                 while (parent && this.patterns.transparent.test(val)) {
3678                     parent = parent.parentNode;
3679                     val = fly(parent).getStyle(attr);
3680                     if (parent.tagName.toUpperCase() == 'HTML') {
3681                         val = '#fff';
3682                     }
3683                 }
3684             }
3685         } else {
3686             val = superclass.getAttribute.call(this, attr);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.doMethod = function(attr, start, end) {
3693         var val;
3694
3695         if (this.patterns.color.test(attr)) {
3696             val = [];
3697             for (var i = 0, len = start.length; i < len; ++i) {
3698                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3699             }
3700
3701             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3702         }
3703         else {
3704             val = superclass.doMethod.call(this, attr, start, end);
3705         }
3706
3707         return val;
3708     };
3709
3710     proto.setRuntimeAttribute = function(attr) {
3711         superclass.setRuntimeAttribute.call(this, attr);
3712
3713         if (this.patterns.color.test(attr)) {
3714             var attributes = this.attributes;
3715             var start = this.parseColor(this.runtimeAttributes[attr].start);
3716             var end = this.parseColor(this.runtimeAttributes[attr].end);
3717
3718             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3719                 end = this.parseColor(attributes[attr].by);
3720
3721                 for (var i = 0, len = start.length; i < len; ++i) {
3722                     end[i] = start[i] + end[i];
3723                 }
3724             }
3725
3726             this.runtimeAttributes[attr].start = start;
3727             this.runtimeAttributes[attr].end = end;
3728         }
3729     };
3730 })();
3731
3732 /*
3733  * Portions of this file are based on pieces of Yahoo User Interface Library
3734  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3735  * YUI licensed under the BSD License:
3736  * http://developer.yahoo.net/yui/license.txt
3737  * <script type="text/javascript">
3738  *
3739  */
3740 Roo.lib.Easing = {
3741
3742
3743     easeNone: function (t, b, c, d) {
3744         return c * t / d + b;
3745     },
3746
3747
3748     easeIn: function (t, b, c, d) {
3749         return c * (t /= d) * t + b;
3750     },
3751
3752
3753     easeOut: function (t, b, c, d) {
3754         return -c * (t /= d) * (t - 2) + b;
3755     },
3756
3757
3758     easeBoth: function (t, b, c, d) {
3759         if ((t /= d / 2) < 1) {
3760             return c / 2 * t * t + b;
3761         }
3762
3763         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3764     },
3765
3766
3767     easeInStrong: function (t, b, c, d) {
3768         return c * (t /= d) * t * t * t + b;
3769     },
3770
3771
3772     easeOutStrong: function (t, b, c, d) {
3773         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3774     },
3775
3776
3777     easeBothStrong: function (t, b, c, d) {
3778         if ((t /= d / 2) < 1) {
3779             return c / 2 * t * t * t * t + b;
3780         }
3781
3782         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3783     },
3784
3785
3786
3787     elasticIn: function (t, b, c, d, a, p) {
3788         if (t == 0) {
3789             return b;
3790         }
3791         if ((t /= d) == 1) {
3792             return b + c;
3793         }
3794         if (!p) {
3795             p = d * .3;
3796         }
3797
3798         if (!a || a < Math.abs(c)) {
3799             a = c;
3800             var s = p / 4;
3801         }
3802         else {
3803             var s = p / (2 * Math.PI) * Math.asin(c / a);
3804         }
3805
3806         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3807     },
3808
3809
3810     elasticOut: function (t, b, c, d, a, p) {
3811         if (t == 0) {
3812             return b;
3813         }
3814         if ((t /= d) == 1) {
3815             return b + c;
3816         }
3817         if (!p) {
3818             p = d * .3;
3819         }
3820
3821         if (!a || a < Math.abs(c)) {
3822             a = c;
3823             var s = p / 4;
3824         }
3825         else {
3826             var s = p / (2 * Math.PI) * Math.asin(c / a);
3827         }
3828
3829         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3830     },
3831
3832
3833     elasticBoth: function (t, b, c, d, a, p) {
3834         if (t == 0) {
3835             return b;
3836         }
3837
3838         if ((t /= d / 2) == 2) {
3839             return b + c;
3840         }
3841
3842         if (!p) {
3843             p = d * (.3 * 1.5);
3844         }
3845
3846         if (!a || a < Math.abs(c)) {
3847             a = c;
3848             var s = p / 4;
3849         }
3850         else {
3851             var s = p / (2 * Math.PI) * Math.asin(c / a);
3852         }
3853
3854         if (t < 1) {
3855             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3856                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3857         }
3858         return a * Math.pow(2, -10 * (t -= 1)) *
3859                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3860     },
3861
3862
3863
3864     backIn: function (t, b, c, d, s) {
3865         if (typeof s == 'undefined') {
3866             s = 1.70158;
3867         }
3868         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3869     },
3870
3871
3872     backOut: function (t, b, c, d, s) {
3873         if (typeof s == 'undefined') {
3874             s = 1.70158;
3875         }
3876         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3877     },
3878
3879
3880     backBoth: function (t, b, c, d, s) {
3881         if (typeof s == 'undefined') {
3882             s = 1.70158;
3883         }
3884
3885         if ((t /= d / 2 ) < 1) {
3886             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3887         }
3888         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3889     },
3890
3891
3892     bounceIn: function (t, b, c, d) {
3893         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3894     },
3895
3896
3897     bounceOut: function (t, b, c, d) {
3898         if ((t /= d) < (1 / 2.75)) {
3899             return c * (7.5625 * t * t) + b;
3900         } else if (t < (2 / 2.75)) {
3901             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3902         } else if (t < (2.5 / 2.75)) {
3903             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3904         }
3905         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3906     },
3907
3908
3909     bounceBoth: function (t, b, c, d) {
3910         if (t < d / 2) {
3911             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3912         }
3913         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3914     }
3915 };/*
3916  * Portions of this file are based on pieces of Yahoo User Interface Library
3917  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3918  * YUI licensed under the BSD License:
3919  * http://developer.yahoo.net/yui/license.txt
3920  * <script type="text/javascript">
3921  *
3922  */
3923     (function() {
3924         Roo.lib.Motion = function(el, attributes, duration, method) {
3925             if (el) {
3926                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3927             }
3928         };
3929
3930         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3931
3932
3933         var Y = Roo.lib;
3934         var superclass = Y.Motion.superclass;
3935         var proto = Y.Motion.prototype;
3936
3937         proto.toString = function() {
3938             var el = this.getEl();
3939             var id = el.id || el.tagName;
3940             return ("Motion " + id);
3941         };
3942
3943         proto.patterns.points = /^points$/i;
3944
3945         proto.setAttribute = function(attr, val, unit) {
3946             if (this.patterns.points.test(attr)) {
3947                 unit = unit || 'px';
3948                 superclass.setAttribute.call(this, 'left', val[0], unit);
3949                 superclass.setAttribute.call(this, 'top', val[1], unit);
3950             } else {
3951                 superclass.setAttribute.call(this, attr, val, unit);
3952             }
3953         };
3954
3955         proto.getAttribute = function(attr) {
3956             if (this.patterns.points.test(attr)) {
3957                 var val = [
3958                         superclass.getAttribute.call(this, 'left'),
3959                         superclass.getAttribute.call(this, 'top')
3960                         ];
3961             } else {
3962                 val = superclass.getAttribute.call(this, attr);
3963             }
3964
3965             return val;
3966         };
3967
3968         proto.doMethod = function(attr, start, end) {
3969             var val = null;
3970
3971             if (this.patterns.points.test(attr)) {
3972                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3973                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3974             } else {
3975                 val = superclass.doMethod.call(this, attr, start, end);
3976             }
3977             return val;
3978         };
3979
3980         proto.setRuntimeAttribute = function(attr) {
3981             if (this.patterns.points.test(attr)) {
3982                 var el = this.getEl();
3983                 var attributes = this.attributes;
3984                 var start;
3985                 var control = attributes['points']['control'] || [];
3986                 var end;
3987                 var i, len;
3988
3989                 if (control.length > 0 && !(control[0] instanceof Array)) {
3990                     control = [control];
3991                 } else {
3992                     var tmp = [];
3993                     for (i = 0,len = control.length; i < len; ++i) {
3994                         tmp[i] = control[i];
3995                     }
3996                     control = tmp;
3997                 }
3998
3999                 Roo.fly(el).position();
4000
4001                 if (isset(attributes['points']['from'])) {
4002                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4003                 }
4004                 else {
4005                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4006                 }
4007
4008                 start = this.getAttribute('points');
4009
4010
4011                 if (isset(attributes['points']['to'])) {
4012                     end = translateValues.call(this, attributes['points']['to'], start);
4013
4014                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4015                     for (i = 0,len = control.length; i < len; ++i) {
4016                         control[i] = translateValues.call(this, control[i], start);
4017                     }
4018
4019
4020                 } else if (isset(attributes['points']['by'])) {
4021                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4022
4023                     for (i = 0,len = control.length; i < len; ++i) {
4024                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4025                     }
4026                 }
4027
4028                 this.runtimeAttributes[attr] = [start];
4029
4030                 if (control.length > 0) {
4031                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4032                 }
4033
4034                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4035             }
4036             else {
4037                 superclass.setRuntimeAttribute.call(this, attr);
4038             }
4039         };
4040
4041         var translateValues = function(val, start) {
4042             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4043             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4044
4045             return val;
4046         };
4047
4048         var isset = function(prop) {
4049             return (typeof prop !== 'undefined');
4050         };
4051     })();
4052 /*
4053  * Portions of this file are based on pieces of Yahoo User Interface Library
4054  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4055  * YUI licensed under the BSD License:
4056  * http://developer.yahoo.net/yui/license.txt
4057  * <script type="text/javascript">
4058  *
4059  */
4060     (function() {
4061         Roo.lib.Scroll = function(el, attributes, duration, method) {
4062             if (el) {
4063                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4064             }
4065         };
4066
4067         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4068
4069
4070         var Y = Roo.lib;
4071         var superclass = Y.Scroll.superclass;
4072         var proto = Y.Scroll.prototype;
4073
4074         proto.toString = function() {
4075             var el = this.getEl();
4076             var id = el.id || el.tagName;
4077             return ("Scroll " + id);
4078         };
4079
4080         proto.doMethod = function(attr, start, end) {
4081             var val = null;
4082
4083             if (attr == 'scroll') {
4084                 val = [
4085                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4086                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4087                         ];
4088
4089             } else {
4090                 val = superclass.doMethod.call(this, attr, start, end);
4091             }
4092             return val;
4093         };
4094
4095         proto.getAttribute = function(attr) {
4096             var val = null;
4097             var el = this.getEl();
4098
4099             if (attr == 'scroll') {
4100                 val = [ el.scrollLeft, el.scrollTop ];
4101             } else {
4102                 val = superclass.getAttribute.call(this, attr);
4103             }
4104
4105             return val;
4106         };
4107
4108         proto.setAttribute = function(attr, val, unit) {
4109             var el = this.getEl();
4110
4111             if (attr == 'scroll') {
4112                 el.scrollLeft = val[0];
4113                 el.scrollTop = val[1];
4114             } else {
4115                 superclass.setAttribute.call(this, attr, val, unit);
4116             }
4117         };
4118     })();
4119 /*
4120  * Based on:
4121  * Ext JS Library 1.1.1
4122  * Copyright(c) 2006-2007, Ext JS, LLC.
4123  *
4124  * Originally Released Under LGPL - original licence link has changed is not relivant.
4125  *
4126  * Fork - LGPL
4127  * <script type="text/javascript">
4128  */
4129
4130
4131 // nasty IE9 hack - what a pile of crap that is..
4132
4133  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4134     Range.prototype.createContextualFragment = function (html) {
4135         var doc = window.document;
4136         var container = doc.createElement("div");
4137         container.innerHTML = html;
4138         var frag = doc.createDocumentFragment(), n;
4139         while ((n = container.firstChild)) {
4140             frag.appendChild(n);
4141         }
4142         return frag;
4143     };
4144 }
4145
4146 /**
4147  * @class Roo.DomHelper
4148  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4149  * 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>.
4150  * @singleton
4151  */
4152 Roo.DomHelper = function(){
4153     var tempTableEl = null;
4154     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4155     var tableRe = /^table|tbody|tr|td$/i;
4156     var xmlns = {};
4157     // build as innerHTML where available
4158     /** @ignore */
4159     var createHtml = function(o){
4160         if(typeof o == 'string'){
4161             return o;
4162         }
4163         var b = "";
4164         if(!o.tag){
4165             o.tag = "div";
4166         }
4167         b += "<" + o.tag;
4168         for(var attr in o){
4169             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4170             if(attr == "style"){
4171                 var s = o["style"];
4172                 if(typeof s == "function"){
4173                     s = s.call();
4174                 }
4175                 if(typeof s == "string"){
4176                     b += ' style="' + s + '"';
4177                 }else if(typeof s == "object"){
4178                     b += ' style="';
4179                     for(var key in s){
4180                         if(typeof s[key] != "function"){
4181                             b += key + ":" + s[key] + ";";
4182                         }
4183                     }
4184                     b += '"';
4185                 }
4186             }else{
4187                 if(attr == "cls"){
4188                     b += ' class="' + o["cls"] + '"';
4189                 }else if(attr == "htmlFor"){
4190                     b += ' for="' + o["htmlFor"] + '"';
4191                 }else{
4192                     b += " " + attr + '="' + o[attr] + '"';
4193                 }
4194             }
4195         }
4196         if(emptyTags.test(o.tag)){
4197             b += "/>";
4198         }else{
4199             b += ">";
4200             var cn = o.children || o.cn;
4201             if(cn){
4202                 //http://bugs.kde.org/show_bug.cgi?id=71506
4203                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4204                     for(var i = 0, len = cn.length; i < len; i++) {
4205                         b += createHtml(cn[i], b);
4206                     }
4207                 }else{
4208                     b += createHtml(cn, b);
4209                 }
4210             }
4211             if(o.html){
4212                 b += o.html;
4213             }
4214             b += "</" + o.tag + ">";
4215         }
4216         return b;
4217     };
4218
4219     // build as dom
4220     /** @ignore */
4221     var createDom = function(o, parentNode){
4222          
4223         // defininition craeted..
4224         var ns = false;
4225         if (o.ns && o.ns != 'html') {
4226                
4227             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4228                 xmlns[o.ns] = o.xmlns;
4229                 ns = o.xmlns;
4230             }
4231             if (typeof(xmlns[o.ns]) == 'undefined') {
4232                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4233             }
4234             ns = xmlns[o.ns];
4235         }
4236         
4237         
4238         if (typeof(o) == 'string') {
4239             return parentNode.appendChild(document.createTextNode(o));
4240         }
4241         o.tag = o.tag || div;
4242         if (o.ns && Roo.isIE) {
4243             ns = false;
4244             o.tag = o.ns + ':' + o.tag;
4245             
4246         }
4247         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4248         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4249         for(var attr in o){
4250             
4251             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4252                     attr == "style" || typeof o[attr] == "function") { continue; }
4253                     
4254             if(attr=="cls" && Roo.isIE){
4255                 el.className = o["cls"];
4256             }else{
4257                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4258                 else { 
4259                     el[attr] = o[attr];
4260                 }
4261             }
4262         }
4263         Roo.DomHelper.applyStyles(el, o.style);
4264         var cn = o.children || o.cn;
4265         if(cn){
4266             //http://bugs.kde.org/show_bug.cgi?id=71506
4267              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4268                 for(var i = 0, len = cn.length; i < len; i++) {
4269                     createDom(cn[i], el);
4270                 }
4271             }else{
4272                 createDom(cn, el);
4273             }
4274         }
4275         if(o.html){
4276             el.innerHTML = o.html;
4277         }
4278         if(parentNode){
4279            parentNode.appendChild(el);
4280         }
4281         return el;
4282     };
4283
4284     var ieTable = function(depth, s, h, e){
4285         tempTableEl.innerHTML = [s, h, e].join('');
4286         var i = -1, el = tempTableEl;
4287         while(++i < depth){
4288             el = el.firstChild;
4289         }
4290         return el;
4291     };
4292
4293     // kill repeat to save bytes
4294     var ts = '<table>',
4295         te = '</table>',
4296         tbs = ts+'<tbody>',
4297         tbe = '</tbody>'+te,
4298         trs = tbs + '<tr>',
4299         tre = '</tr>'+tbe;
4300
4301     /**
4302      * @ignore
4303      * Nasty code for IE's broken table implementation
4304      */
4305     var insertIntoTable = function(tag, where, el, html){
4306         if(!tempTableEl){
4307             tempTableEl = document.createElement('div');
4308         }
4309         var node;
4310         var before = null;
4311         if(tag == 'td'){
4312             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4313                 return;
4314             }
4315             if(where == 'beforebegin'){
4316                 before = el;
4317                 el = el.parentNode;
4318             } else{
4319                 before = el.nextSibling;
4320                 el = el.parentNode;
4321             }
4322             node = ieTable(4, trs, html, tre);
4323         }
4324         else if(tag == 'tr'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(3, tbs, html, tbe);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(3, tbs, html, tbe);
4333             } else{ // INTO a TR
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(4, trs, html, tre);
4338             }
4339         } else if(tag == 'tbody'){
4340             if(where == 'beforebegin'){
4341                 before = el;
4342                 el = el.parentNode;
4343                 node = ieTable(2, ts, html, te);
4344             } else if(where == 'afterend'){
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347                 node = ieTable(2, ts, html, te);
4348             } else{
4349                 if(where == 'afterbegin'){
4350                     before = el.firstChild;
4351                 }
4352                 node = ieTable(3, tbs, html, tbe);
4353             }
4354         } else{ // TABLE
4355             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4356                 return;
4357             }
4358             if(where == 'afterbegin'){
4359                 before = el.firstChild;
4360             }
4361             node = ieTable(2, ts, html, te);
4362         }
4363         el.insertBefore(node, before);
4364         return node;
4365     };
4366
4367     return {
4368     /** True to force the use of DOM instead of html fragments @type Boolean */
4369     useDom : false,
4370
4371     /**
4372      * Returns the markup for the passed Element(s) config
4373      * @param {Object} o The Dom object spec (and children)
4374      * @return {String}
4375      */
4376     markup : function(o){
4377         return createHtml(o);
4378     },
4379
4380     /**
4381      * Applies a style specification to an element
4382      * @param {String/HTMLElement} el The element to apply styles to
4383      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4384      * a function which returns such a specification.
4385      */
4386     applyStyles : function(el, styles){
4387         if(styles){
4388            el = Roo.fly(el);
4389            if(typeof styles == "string"){
4390                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4391                var matches;
4392                while ((matches = re.exec(styles)) != null){
4393                    el.setStyle(matches[1], matches[2]);
4394                }
4395            }else if (typeof styles == "object"){
4396                for (var style in styles){
4397                   el.setStyle(style, styles[style]);
4398                }
4399            }else if (typeof styles == "function"){
4400                 Roo.DomHelper.applyStyles(el, styles.call());
4401            }
4402         }
4403     },
4404
4405     /**
4406      * Inserts an HTML fragment into the Dom
4407      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4408      * @param {HTMLElement} el The context element
4409      * @param {String} html The HTML fragmenet
4410      * @return {HTMLElement} The new node
4411      */
4412     insertHtml : function(where, el, html){
4413         where = where.toLowerCase();
4414         if(el.insertAdjacentHTML){
4415             if(tableRe.test(el.tagName)){
4416                 var rs;
4417                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4418                     return rs;
4419                 }
4420             }
4421             switch(where){
4422                 case "beforebegin":
4423                     el.insertAdjacentHTML('BeforeBegin', html);
4424                     return el.previousSibling;
4425                 case "afterbegin":
4426                     el.insertAdjacentHTML('AfterBegin', html);
4427                     return el.firstChild;
4428                 case "beforeend":
4429                     el.insertAdjacentHTML('BeforeEnd', html);
4430                     return el.lastChild;
4431                 case "afterend":
4432                     el.insertAdjacentHTML('AfterEnd', html);
4433                     return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436         }
4437         var range = el.ownerDocument.createRange();
4438         var frag;
4439         switch(where){
4440              case "beforebegin":
4441                 range.setStartBefore(el);
4442                 frag = range.createContextualFragment(html);
4443                 el.parentNode.insertBefore(frag, el);
4444                 return el.previousSibling;
4445              case "afterbegin":
4446                 if(el.firstChild){
4447                     range.setStartBefore(el.firstChild);
4448                     frag = range.createContextualFragment(html);
4449                     el.insertBefore(frag, el.firstChild);
4450                     return el.firstChild;
4451                 }else{
4452                     el.innerHTML = html;
4453                     return el.firstChild;
4454                 }
4455             case "beforeend":
4456                 if(el.lastChild){
4457                     range.setStartAfter(el.lastChild);
4458                     frag = range.createContextualFragment(html);
4459                     el.appendChild(frag);
4460                     return el.lastChild;
4461                 }else{
4462                     el.innerHTML = html;
4463                     return el.lastChild;
4464                 }
4465             case "afterend":
4466                 range.setStartAfter(el);
4467                 frag = range.createContextualFragment(html);
4468                 el.parentNode.insertBefore(frag, el.nextSibling);
4469                 return el.nextSibling;
4470             }
4471             throw 'Illegal insertion point -> "' + where + '"';
4472     },
4473
4474     /**
4475      * Creates new Dom element(s) and inserts them before el
4476      * @param {String/HTMLElement/Element} el The context element
4477      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4478      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4479      * @return {HTMLElement/Roo.Element} The new node
4480      */
4481     insertBefore : function(el, o, returnElement){
4482         return this.doInsert(el, o, returnElement, "beforeBegin");
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and inserts them after el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object} o The Dom object spec (and children)
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     insertAfter : function(el, o, returnElement){
4493         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4494     },
4495
4496     /**
4497      * Creates new Dom element(s) and inserts them as the first child of el
4498      * @param {String/HTMLElement/Element} el The context element
4499      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4500      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4501      * @return {HTMLElement/Roo.Element} The new node
4502      */
4503     insertFirst : function(el, o, returnElement){
4504         return this.doInsert(el, o, returnElement, "afterBegin");
4505     },
4506
4507     // private
4508     doInsert : function(el, o, returnElement, pos, sibling){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml(pos, el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and appends them to el
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     append : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         var newNode;
4531         if(this.useDom || o.ns){
4532             newNode = createDom(o, null);
4533             el.appendChild(newNode);
4534         }else{
4535             var html = createHtml(o);
4536             newNode = this.insertHtml("beforeEnd", el, html);
4537         }
4538         return returnElement ? Roo.get(newNode, true) : newNode;
4539     },
4540
4541     /**
4542      * Creates new Dom element(s) and overwrites the contents of el with them
4543      * @param {String/HTMLElement/Element} el The context element
4544      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4545      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4546      * @return {HTMLElement/Roo.Element} The new node
4547      */
4548     overwrite : function(el, o, returnElement){
4549         el = Roo.getDom(el);
4550         if (o.ns) {
4551           
4552             while (el.childNodes.length) {
4553                 el.removeChild(el.firstChild);
4554             }
4555             createDom(o, el);
4556         } else {
4557             el.innerHTML = createHtml(o);   
4558         }
4559         
4560         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4561     },
4562
4563     /**
4564      * Creates a new Roo.DomHelper.Template from the Dom object spec
4565      * @param {Object} o The Dom object spec (and children)
4566      * @return {Roo.DomHelper.Template} The new template
4567      */
4568     createTemplate : function(o){
4569         var html = createHtml(o);
4570         return new Roo.Template(html);
4571     }
4572     };
4573 }();
4574 /*
4575  * Based on:
4576  * Ext JS Library 1.1.1
4577  * Copyright(c) 2006-2007, Ext JS, LLC.
4578  *
4579  * Originally Released Under LGPL - original licence link has changed is not relivant.
4580  *
4581  * Fork - LGPL
4582  * <script type="text/javascript">
4583  */
4584  
4585 /**
4586 * @class Roo.Template
4587 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4588 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4589 * Usage:
4590 <pre><code>
4591 var t = new Roo.Template({
4592     html :  '&lt;div name="{id}"&gt;' + 
4593         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4594         '&lt;/div&gt;',
4595     myformat: function (value, allValues) {
4596         return 'XX' + value;
4597     }
4598 });
4599 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4600 </code></pre>
4601 * For more information see this blog post with examples:
4602 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4603      - Create Elements using DOM, HTML fragments and Templates</a>. 
4604 * @constructor
4605 * @param {Object} cfg - Configuration object.
4606 */
4607 Roo.Template = function(cfg){
4608     // BC!
4609     if(cfg instanceof Array){
4610         cfg = cfg.join("");
4611     }else if(arguments.length > 1){
4612         cfg = Array.prototype.join.call(arguments, "");
4613     }
4614     
4615     
4616     if (typeof(cfg) == 'object') {
4617         Roo.apply(this,cfg)
4618     } else {
4619         // bc
4620         this.html = cfg;
4621     }
4622     if (this.url) {
4623         this.load();
4624     }
4625     
4626 };
4627 Roo.Template.prototype = {
4628     
4629     /**
4630      * @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..
4631      *                    it should be fixed so that template is observable...
4632      */
4633     url : false,
4634     /**
4635      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4636      */
4637     html : '',
4638     /**
4639      * Returns an HTML fragment of this template with the specified values applied.
4640      * @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'})
4641      * @return {String} The HTML fragment
4642      */
4643     applyTemplate : function(values){
4644         try {
4645            
4646             if(this.compiled){
4647                 return this.compiled(values);
4648             }
4649             var useF = this.disableFormats !== true;
4650             var fm = Roo.util.Format, tpl = this;
4651             var fn = function(m, name, format, args){
4652                 if(format && useF){
4653                     if(format.substr(0, 5) == "this."){
4654                         return tpl.call(format.substr(5), values[name], values);
4655                     }else{
4656                         if(args){
4657                             // quoted values are required for strings in compiled templates, 
4658                             // but for non compiled we need to strip them
4659                             // quoted reversed for jsmin
4660                             var re = /^\s*['"](.*)["']\s*$/;
4661                             args = args.split(',');
4662                             for(var i = 0, len = args.length; i < len; i++){
4663                                 args[i] = args[i].replace(re, "$1");
4664                             }
4665                             args = [values[name]].concat(args);
4666                         }else{
4667                             args = [values[name]];
4668                         }
4669                         return fm[format].apply(fm, args);
4670                     }
4671                 }else{
4672                     return values[name] !== undefined ? values[name] : "";
4673                 }
4674             };
4675             return this.html.replace(this.re, fn);
4676         } catch (e) {
4677             Roo.log(e);
4678             throw e;
4679         }
4680          
4681     },
4682     
4683     loading : false,
4684       
4685     load : function ()
4686     {
4687          
4688         if (this.loading) {
4689             return;
4690         }
4691         var _t = this;
4692         
4693         this.loading = true;
4694         this.compiled = false;
4695         
4696         var cx = new Roo.data.Connection();
4697         cx.request({
4698             url : this.url,
4699             method : 'GET',
4700             success : function (response) {
4701                 _t.loading = false;
4702                 _t.html = response.responseText;
4703                 _t.url = false;
4704                 _t.compile();
4705              },
4706             failure : function(response) {
4707                 Roo.log("Template failed to load from " + _t.url);
4708                 _t.loading = false;
4709             }
4710         });
4711     },
4712
4713     /**
4714      * Sets the HTML used as the template and optionally compiles it.
4715      * @param {String} html
4716      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4717      * @return {Roo.Template} this
4718      */
4719     set : function(html, compile){
4720         this.html = html;
4721         this.compiled = null;
4722         if(compile){
4723             this.compile();
4724         }
4725         return this;
4726     },
4727     
4728     /**
4729      * True to disable format functions (defaults to false)
4730      * @type Boolean
4731      */
4732     disableFormats : false,
4733     
4734     /**
4735     * The regular expression used to match template variables 
4736     * @type RegExp
4737     * @property 
4738     */
4739     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4740     
4741     /**
4742      * Compiles the template into an internal function, eliminating the RegEx overhead.
4743      * @return {Roo.Template} this
4744      */
4745     compile : function(){
4746         var fm = Roo.util.Format;
4747         var useF = this.disableFormats !== true;
4748         var sep = Roo.isGecko ? "+" : ",";
4749         var fn = function(m, name, format, args){
4750             if(format && useF){
4751                 args = args ? ',' + args : "";
4752                 if(format.substr(0, 5) != "this."){
4753                     format = "fm." + format + '(';
4754                 }else{
4755                     format = 'this.call("'+ format.substr(5) + '", ';
4756                     args = ", values";
4757                 }
4758             }else{
4759                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4760             }
4761             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4762         };
4763         var body;
4764         // branched to use + in gecko and [].join() in others
4765         if(Roo.isGecko){
4766             body = "this.compiled = function(values){ return '" +
4767                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4768                     "';};";
4769         }else{
4770             body = ["this.compiled = function(values){ return ['"];
4771             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4772             body.push("'].join('');};");
4773             body = body.join('');
4774         }
4775         /**
4776          * eval:var:values
4777          * eval:var:fm
4778          */
4779         eval(body);
4780         return this;
4781     },
4782     
4783     // private function used to call members
4784     call : function(fnName, value, allValues){
4785         return this[fnName](value, allValues);
4786     },
4787     
4788     /**
4789      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4790      * @param {String/HTMLElement/Roo.Element} el The context element
4791      * @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'})
4792      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4793      * @return {HTMLElement/Roo.Element} The new node or Element
4794      */
4795     insertFirst: function(el, values, returnElement){
4796         return this.doInsert('afterBegin', el, values, returnElement);
4797     },
4798
4799     /**
4800      * Applies the supplied values to the template and inserts the new node(s) before el.
4801      * @param {String/HTMLElement/Roo.Element} el The context element
4802      * @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'})
4803      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4804      * @return {HTMLElement/Roo.Element} The new node or Element
4805      */
4806     insertBefore: function(el, values, returnElement){
4807         return this.doInsert('beforeBegin', el, values, returnElement);
4808     },
4809
4810     /**
4811      * Applies the supplied values to the template and inserts the new node(s) after el.
4812      * @param {String/HTMLElement/Roo.Element} el The context element
4813      * @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'})
4814      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4815      * @return {HTMLElement/Roo.Element} The new node or Element
4816      */
4817     insertAfter : function(el, values, returnElement){
4818         return this.doInsert('afterEnd', el, values, returnElement);
4819     },
4820     
4821     /**
4822      * Applies the supplied values to the template and appends the new node(s) to el.
4823      * @param {String/HTMLElement/Roo.Element} el The context element
4824      * @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'})
4825      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4826      * @return {HTMLElement/Roo.Element} The new node or Element
4827      */
4828     append : function(el, values, returnElement){
4829         return this.doInsert('beforeEnd', el, values, returnElement);
4830     },
4831
4832     doInsert : function(where, el, values, returnEl){
4833         el = Roo.getDom(el);
4834         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4835         return returnEl ? Roo.get(newNode, true) : newNode;
4836     },
4837
4838     /**
4839      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4840      * @param {String/HTMLElement/Roo.Element} el The context element
4841      * @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'})
4842      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4843      * @return {HTMLElement/Roo.Element} The new node or Element
4844      */
4845     overwrite : function(el, values, returnElement){
4846         el = Roo.getDom(el);
4847         el.innerHTML = this.applyTemplate(values);
4848         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4849     }
4850 };
4851 /**
4852  * Alias for {@link #applyTemplate}
4853  * @method
4854  */
4855 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4856
4857 // backwards compat
4858 Roo.DomHelper.Template = Roo.Template;
4859
4860 /**
4861  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4862  * @param {String/HTMLElement} el A DOM element or its id
4863  * @returns {Roo.Template} The created template
4864  * @static
4865  */
4866 Roo.Template.from = function(el){
4867     el = Roo.getDom(el);
4868     return new Roo.Template(el.value || el.innerHTML);
4869 };/*
4870  * Based on:
4871  * Ext JS Library 1.1.1
4872  * Copyright(c) 2006-2007, Ext JS, LLC.
4873  *
4874  * Originally Released Under LGPL - original licence link has changed is not relivant.
4875  *
4876  * Fork - LGPL
4877  * <script type="text/javascript">
4878  */
4879  
4880
4881 /*
4882  * This is code is also distributed under MIT license for use
4883  * with jQuery and prototype JavaScript libraries.
4884  */
4885 /**
4886  * @class Roo.DomQuery
4887 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).
4888 <p>
4889 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>
4890
4891 <p>
4892 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.
4893 </p>
4894 <h4>Element Selectors:</h4>
4895 <ul class="list">
4896     <li> <b>*</b> any element</li>
4897     <li> <b>E</b> an element with the tag E</li>
4898     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4899     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4900     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4901     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4902 </ul>
4903 <h4>Attribute Selectors:</h4>
4904 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4905 <ul class="list">
4906     <li> <b>E[foo]</b> has an attribute "foo"</li>
4907     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4908     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4909     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4910     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4911     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4912     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4913 </ul>
4914 <h4>Pseudo Classes:</h4>
4915 <ul class="list">
4916     <li> <b>E:first-child</b> E is the first child of its parent</li>
4917     <li> <b>E:last-child</b> E is the last child of its parent</li>
4918     <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>
4919     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4920     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4921     <li> <b>E:only-child</b> E is the only child of its parent</li>
4922     <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>
4923     <li> <b>E:first</b> the first E in the resultset</li>
4924     <li> <b>E:last</b> the last E in the resultset</li>
4925     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4926     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4927     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4928     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4929     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4930     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4931     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4932     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4933     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4934 </ul>
4935 <h4>CSS Value Selectors:</h4>
4936 <ul class="list">
4937     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4938     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4939     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4940     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4941     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4942     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4943 </ul>
4944  * @singleton
4945  */
4946 Roo.DomQuery = function(){
4947     var cache = {}, simpleCache = {}, valueCache = {};
4948     var nonSpace = /\S/;
4949     var trimRe = /^\s+|\s+$/g;
4950     var tplRe = /\{(\d+)\}/g;
4951     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4952     var tagTokenRe = /^(#)?([\w-\*]+)/;
4953     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4954
4955     function child(p, index){
4956         var i = 0;
4957         var n = p.firstChild;
4958         while(n){
4959             if(n.nodeType == 1){
4960                if(++i == index){
4961                    return n;
4962                }
4963             }
4964             n = n.nextSibling;
4965         }
4966         return null;
4967     };
4968
4969     function next(n){
4970         while((n = n.nextSibling) && n.nodeType != 1);
4971         return n;
4972     };
4973
4974     function prev(n){
4975         while((n = n.previousSibling) && n.nodeType != 1);
4976         return n;
4977     };
4978
4979     function children(d){
4980         var n = d.firstChild, ni = -1;
4981             while(n){
4982                 var nx = n.nextSibling;
4983                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4984                     d.removeChild(n);
4985                 }else{
4986                     n.nodeIndex = ++ni;
4987                 }
4988                 n = nx;
4989             }
4990             return this;
4991         };
4992
4993     function byClassName(c, a, v){
4994         if(!v){
4995             return c;
4996         }
4997         var r = [], ri = -1, cn;
4998         for(var i = 0, ci; ci = c[i]; i++){
4999             if((' '+ci.className+' ').indexOf(v) != -1){
5000                 r[++ri] = ci;
5001             }
5002         }
5003         return r;
5004     };
5005
5006     function attrValue(n, attr){
5007         if(!n.tagName && typeof n.length != "undefined"){
5008             n = n[0];
5009         }
5010         if(!n){
5011             return null;
5012         }
5013         if(attr == "for"){
5014             return n.htmlFor;
5015         }
5016         if(attr == "class" || attr == "className"){
5017             return n.className;
5018         }
5019         return n.getAttribute(attr) || n[attr];
5020
5021     };
5022
5023     function getNodes(ns, mode, tagName){
5024         var result = [], ri = -1, cs;
5025         if(!ns){
5026             return result;
5027         }
5028         tagName = tagName || "*";
5029         if(typeof ns.getElementsByTagName != "undefined"){
5030             ns = [ns];
5031         }
5032         if(!mode){
5033             for(var i = 0, ni; ni = ns[i]; i++){
5034                 cs = ni.getElementsByTagName(tagName);
5035                 for(var j = 0, ci; ci = cs[j]; j++){
5036                     result[++ri] = ci;
5037                 }
5038             }
5039         }else if(mode == "/" || mode == ">"){
5040             var utag = tagName.toUpperCase();
5041             for(var i = 0, ni, cn; ni = ns[i]; i++){
5042                 cn = ni.children || ni.childNodes;
5043                 for(var j = 0, cj; cj = cn[j]; j++){
5044                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5045                         result[++ri] = cj;
5046                     }
5047                 }
5048             }
5049         }else if(mode == "+"){
5050             var utag = tagName.toUpperCase();
5051             for(var i = 0, n; n = ns[i]; i++){
5052                 while((n = n.nextSibling) && n.nodeType != 1);
5053                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5054                     result[++ri] = n;
5055                 }
5056             }
5057         }else if(mode == "~"){
5058             for(var i = 0, n; n = ns[i]; i++){
5059                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5060                 if(n){
5061                     result[++ri] = n;
5062                 }
5063             }
5064         }
5065         return result;
5066     };
5067
5068     function concat(a, b){
5069         if(b.slice){
5070             return a.concat(b);
5071         }
5072         for(var i = 0, l = b.length; i < l; i++){
5073             a[a.length] = b[i];
5074         }
5075         return a;
5076     }
5077
5078     function byTag(cs, tagName){
5079         if(cs.tagName || cs == document){
5080             cs = [cs];
5081         }
5082         if(!tagName){
5083             return cs;
5084         }
5085         var r = [], ri = -1;
5086         tagName = tagName.toLowerCase();
5087         for(var i = 0, ci; ci = cs[i]; i++){
5088             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5089                 r[++ri] = ci;
5090             }
5091         }
5092         return r;
5093     };
5094
5095     function byId(cs, attr, id){
5096         if(cs.tagName || cs == document){
5097             cs = [cs];
5098         }
5099         if(!id){
5100             return cs;
5101         }
5102         var r = [], ri = -1;
5103         for(var i = 0,ci; ci = cs[i]; i++){
5104             if(ci && ci.id == id){
5105                 r[++ri] = ci;
5106                 return r;
5107             }
5108         }
5109         return r;
5110     };
5111
5112     function byAttribute(cs, attr, value, op, custom){
5113         var r = [], ri = -1, st = custom=="{";
5114         var f = Roo.DomQuery.operators[op];
5115         for(var i = 0, ci; ci = cs[i]; i++){
5116             var a;
5117             if(st){
5118                 a = Roo.DomQuery.getStyle(ci, attr);
5119             }
5120             else if(attr == "class" || attr == "className"){
5121                 a = ci.className;
5122             }else if(attr == "for"){
5123                 a = ci.htmlFor;
5124             }else if(attr == "href"){
5125                 a = ci.getAttribute("href", 2);
5126             }else{
5127                 a = ci.getAttribute(attr);
5128             }
5129             if((f && f(a, value)) || (!f && a)){
5130                 r[++ri] = ci;
5131             }
5132         }
5133         return r;
5134     };
5135
5136     function byPseudo(cs, name, value){
5137         return Roo.DomQuery.pseudos[name](cs, value);
5138     };
5139
5140     // This is for IE MSXML which does not support expandos.
5141     // IE runs the same speed using setAttribute, however FF slows way down
5142     // and Safari completely fails so they need to continue to use expandos.
5143     var isIE = window.ActiveXObject ? true : false;
5144
5145     // this eval is stop the compressor from
5146     // renaming the variable to something shorter
5147     
5148     /** eval:var:batch */
5149     var batch = 30803; 
5150
5151     var key = 30803;
5152
5153     function nodupIEXml(cs){
5154         var d = ++key;
5155         cs[0].setAttribute("_nodup", d);
5156         var r = [cs[0]];
5157         for(var i = 1, len = cs.length; i < len; i++){
5158             var c = cs[i];
5159             if(!c.getAttribute("_nodup") != d){
5160                 c.setAttribute("_nodup", d);
5161                 r[r.length] = c;
5162             }
5163         }
5164         for(var i = 0, len = cs.length; i < len; i++){
5165             cs[i].removeAttribute("_nodup");
5166         }
5167         return r;
5168     }
5169
5170     function nodup(cs){
5171         if(!cs){
5172             return [];
5173         }
5174         var len = cs.length, c, i, r = cs, cj, ri = -1;
5175         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5176             return cs;
5177         }
5178         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5179             return nodupIEXml(cs);
5180         }
5181         var d = ++key;
5182         cs[0]._nodup = d;
5183         for(i = 1; c = cs[i]; i++){
5184             if(c._nodup != d){
5185                 c._nodup = d;
5186             }else{
5187                 r = [];
5188                 for(var j = 0; j < i; j++){
5189                     r[++ri] = cs[j];
5190                 }
5191                 for(j = i+1; cj = cs[j]; j++){
5192                     if(cj._nodup != d){
5193                         cj._nodup = d;
5194                         r[++ri] = cj;
5195                     }
5196                 }
5197                 return r;
5198             }
5199         }
5200         return r;
5201     }
5202
5203     function quickDiffIEXml(c1, c2){
5204         var d = ++key;
5205         for(var i = 0, len = c1.length; i < len; i++){
5206             c1[i].setAttribute("_qdiff", d);
5207         }
5208         var r = [];
5209         for(var i = 0, len = c2.length; i < len; i++){
5210             if(c2[i].getAttribute("_qdiff") != d){
5211                 r[r.length] = c2[i];
5212             }
5213         }
5214         for(var i = 0, len = c1.length; i < len; i++){
5215            c1[i].removeAttribute("_qdiff");
5216         }
5217         return r;
5218     }
5219
5220     function quickDiff(c1, c2){
5221         var len1 = c1.length;
5222         if(!len1){
5223             return c2;
5224         }
5225         if(isIE && c1[0].selectSingleNode){
5226             return quickDiffIEXml(c1, c2);
5227         }
5228         var d = ++key;
5229         for(var i = 0; i < len1; i++){
5230             c1[i]._qdiff = d;
5231         }
5232         var r = [];
5233         for(var i = 0, len = c2.length; i < len; i++){
5234             if(c2[i]._qdiff != d){
5235                 r[r.length] = c2[i];
5236             }
5237         }
5238         return r;
5239     }
5240
5241     function quickId(ns, mode, root, id){
5242         if(ns == root){
5243            var d = root.ownerDocument || root;
5244            return d.getElementById(id);
5245         }
5246         ns = getNodes(ns, mode, "*");
5247         return byId(ns, null, id);
5248     }
5249
5250     return {
5251         getStyle : function(el, name){
5252             return Roo.fly(el).getStyle(name);
5253         },
5254         /**
5255          * Compiles a selector/xpath query into a reusable function. The returned function
5256          * takes one parameter "root" (optional), which is the context node from where the query should start.
5257          * @param {String} selector The selector/xpath query
5258          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5259          * @return {Function}
5260          */
5261         compile : function(path, type){
5262             type = type || "select";
5263             
5264             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5265             var q = path, mode, lq;
5266             var tk = Roo.DomQuery.matchers;
5267             var tklen = tk.length;
5268             var mm;
5269
5270             // accept leading mode switch
5271             var lmode = q.match(modeRe);
5272             if(lmode && lmode[1]){
5273                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5274                 q = q.replace(lmode[1], "");
5275             }
5276             // strip leading slashes
5277             while(path.substr(0, 1)=="/"){
5278                 path = path.substr(1);
5279             }
5280
5281             while(q && lq != q){
5282                 lq = q;
5283                 var tm = q.match(tagTokenRe);
5284                 if(type == "select"){
5285                     if(tm){
5286                         if(tm[1] == "#"){
5287                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5288                         }else{
5289                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5290                         }
5291                         q = q.replace(tm[0], "");
5292                     }else if(q.substr(0, 1) != '@'){
5293                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5294                     }
5295                 }else{
5296                     if(tm){
5297                         if(tm[1] == "#"){
5298                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5299                         }else{
5300                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5301                         }
5302                         q = q.replace(tm[0], "");
5303                     }
5304                 }
5305                 while(!(mm = q.match(modeRe))){
5306                     var matched = false;
5307                     for(var j = 0; j < tklen; j++){
5308                         var t = tk[j];
5309                         var m = q.match(t.re);
5310                         if(m){
5311                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5312                                                     return m[i];
5313                                                 });
5314                             q = q.replace(m[0], "");
5315                             matched = true;
5316                             break;
5317                         }
5318                     }
5319                     // prevent infinite loop on bad selector
5320                     if(!matched){
5321                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5322                     }
5323                 }
5324                 if(mm[1]){
5325                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5326                     q = q.replace(mm[1], "");
5327                 }
5328             }
5329             fn[fn.length] = "return nodup(n);\n}";
5330             
5331              /** 
5332               * list of variables that need from compression as they are used by eval.
5333              *  eval:var:batch 
5334              *  eval:var:nodup
5335              *  eval:var:byTag
5336              *  eval:var:ById
5337              *  eval:var:getNodes
5338              *  eval:var:quickId
5339              *  eval:var:mode
5340              *  eval:var:root
5341              *  eval:var:n
5342              *  eval:var:byClassName
5343              *  eval:var:byPseudo
5344              *  eval:var:byAttribute
5345              *  eval:var:attrValue
5346              * 
5347              **/ 
5348             eval(fn.join(""));
5349             return f;
5350         },
5351
5352         /**
5353          * Selects a group of elements.
5354          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5355          * @param {Node} root (optional) The start of the query (defaults to document).
5356          * @return {Array}
5357          */
5358         select : function(path, root, type){
5359             if(!root || root == document){
5360                 root = document;
5361             }
5362             if(typeof root == "string"){
5363                 root = document.getElementById(root);
5364             }
5365             var paths = path.split(",");
5366             var results = [];
5367             for(var i = 0, len = paths.length; i < len; i++){
5368                 var p = paths[i].replace(trimRe, "");
5369                 if(!cache[p]){
5370                     cache[p] = Roo.DomQuery.compile(p);
5371                     if(!cache[p]){
5372                         throw p + " is not a valid selector";
5373                     }
5374                 }
5375                 var result = cache[p](root);
5376                 if(result && result != document){
5377                     results = results.concat(result);
5378                 }
5379             }
5380             if(paths.length > 1){
5381                 return nodup(results);
5382             }
5383             return results;
5384         },
5385
5386         /**
5387          * Selects a single element.
5388          * @param {String} selector The selector/xpath query
5389          * @param {Node} root (optional) The start of the query (defaults to document).
5390          * @return {Element}
5391          */
5392         selectNode : function(path, root){
5393             return Roo.DomQuery.select(path, root)[0];
5394         },
5395
5396         /**
5397          * Selects the value of a node, optionally replacing null with the defaultValue.
5398          * @param {String} selector The selector/xpath query
5399          * @param {Node} root (optional) The start of the query (defaults to document).
5400          * @param {String} defaultValue
5401          */
5402         selectValue : function(path, root, defaultValue){
5403             path = path.replace(trimRe, "");
5404             if(!valueCache[path]){
5405                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5406             }
5407             var n = valueCache[path](root);
5408             n = n[0] ? n[0] : n;
5409             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5410             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5411         },
5412
5413         /**
5414          * Selects the value of a node, parsing integers and floats.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @param {Number} defaultValue
5418          * @return {Number}
5419          */
5420         selectNumber : function(path, root, defaultValue){
5421             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5422             return parseFloat(v);
5423         },
5424
5425         /**
5426          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5427          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5428          * @param {String} selector The simple selector to test
5429          * @return {Boolean}
5430          */
5431         is : function(el, ss){
5432             if(typeof el == "string"){
5433                 el = document.getElementById(el);
5434             }
5435             var isArray = (el instanceof Array);
5436             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5437             return isArray ? (result.length == el.length) : (result.length > 0);
5438         },
5439
5440         /**
5441          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5442          * @param {Array} el An array of elements to filter
5443          * @param {String} selector The simple selector to test
5444          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5445          * the selector instead of the ones that match
5446          * @return {Array}
5447          */
5448         filter : function(els, ss, nonMatches){
5449             ss = ss.replace(trimRe, "");
5450             if(!simpleCache[ss]){
5451                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5452             }
5453             var result = simpleCache[ss](els);
5454             return nonMatches ? quickDiff(result, els) : result;
5455         },
5456
5457         /**
5458          * Collection of matching regular expressions and code snippets.
5459          */
5460         matchers : [{
5461                 re: /^\.([\w-]+)/,
5462                 select: 'n = byClassName(n, null, " {1} ");'
5463             }, {
5464                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5465                 select: 'n = byPseudo(n, "{1}", "{2}");'
5466             },{
5467                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5468                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5469             }, {
5470                 re: /^#([\w-]+)/,
5471                 select: 'n = byId(n, null, "{1}");'
5472             },{
5473                 re: /^@([\w-]+)/,
5474                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5475             }
5476         ],
5477
5478         /**
5479          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5480          * 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;.
5481          */
5482         operators : {
5483             "=" : function(a, v){
5484                 return a == v;
5485             },
5486             "!=" : function(a, v){
5487                 return a != v;
5488             },
5489             "^=" : function(a, v){
5490                 return a && a.substr(0, v.length) == v;
5491             },
5492             "$=" : function(a, v){
5493                 return a && a.substr(a.length-v.length) == v;
5494             },
5495             "*=" : function(a, v){
5496                 return a && a.indexOf(v) !== -1;
5497             },
5498             "%=" : function(a, v){
5499                 return (a % v) == 0;
5500             },
5501             "|=" : function(a, v){
5502                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5503             },
5504             "~=" : function(a, v){
5505                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5506             }
5507         },
5508
5509         /**
5510          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5511          * and the argument (if any) supplied in the selector.
5512          */
5513         pseudos : {
5514             "first-child" : function(c){
5515                 var r = [], ri = -1, n;
5516                 for(var i = 0, ci; ci = n = c[i]; i++){
5517                     while((n = n.previousSibling) && n.nodeType != 1);
5518                     if(!n){
5519                         r[++ri] = ci;
5520                     }
5521                 }
5522                 return r;
5523             },
5524
5525             "last-child" : function(c){
5526                 var r = [], ri = -1, n;
5527                 for(var i = 0, ci; ci = n = c[i]; i++){
5528                     while((n = n.nextSibling) && n.nodeType != 1);
5529                     if(!n){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "nth-child" : function(c, a) {
5537                 var r = [], ri = -1;
5538                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5539                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5540                 for(var i = 0, n; n = c[i]; i++){
5541                     var pn = n.parentNode;
5542                     if (batch != pn._batch) {
5543                         var j = 0;
5544                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5545                             if(cn.nodeType == 1){
5546                                cn.nodeIndex = ++j;
5547                             }
5548                         }
5549                         pn._batch = batch;
5550                     }
5551                     if (f == 1) {
5552                         if (l == 0 || n.nodeIndex == l){
5553                             r[++ri] = n;
5554                         }
5555                     } else if ((n.nodeIndex + l) % f == 0){
5556                         r[++ri] = n;
5557                     }
5558                 }
5559
5560                 return r;
5561             },
5562
5563             "only-child" : function(c){
5564                 var r = [], ri = -1;;
5565                 for(var i = 0, ci; ci = c[i]; i++){
5566                     if(!prev(ci) && !next(ci)){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             },
5572
5573             "empty" : function(c){
5574                 var r = [], ri = -1;
5575                 for(var i = 0, ci; ci = c[i]; i++){
5576                     var cns = ci.childNodes, j = 0, cn, empty = true;
5577                     while(cn = cns[j]){
5578                         ++j;
5579                         if(cn.nodeType == 1 || cn.nodeType == 3){
5580                             empty = false;
5581                             break;
5582                         }
5583                     }
5584                     if(empty){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "contains" : function(c, v){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "nodeValue" : function(c, v){
5602                 var r = [], ri = -1;
5603                 for(var i = 0, ci; ci = c[i]; i++){
5604                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5605                         r[++ri] = ci;
5606                     }
5607                 }
5608                 return r;
5609             },
5610
5611             "checked" : function(c){
5612                 var r = [], ri = -1;
5613                 for(var i = 0, ci; ci = c[i]; i++){
5614                     if(ci.checked == true){
5615                         r[++ri] = ci;
5616                     }
5617                 }
5618                 return r;
5619             },
5620
5621             "not" : function(c, ss){
5622                 return Roo.DomQuery.filter(c, ss, true);
5623             },
5624
5625             "odd" : function(c){
5626                 return this["nth-child"](c, "odd");
5627             },
5628
5629             "even" : function(c){
5630                 return this["nth-child"](c, "even");
5631             },
5632
5633             "nth" : function(c, a){
5634                 return c[a-1] || [];
5635             },
5636
5637             "first" : function(c){
5638                 return c[0] || [];
5639             },
5640
5641             "last" : function(c){
5642                 return c[c.length-1] || [];
5643             },
5644
5645             "has" : function(c, ss){
5646                 var s = Roo.DomQuery.select;
5647                 var r = [], ri = -1;
5648                 for(var i = 0, ci; ci = c[i]; i++){
5649                     if(s(ss, ci).length > 0){
5650                         r[++ri] = ci;
5651                     }
5652                 }
5653                 return r;
5654             },
5655
5656             "next" : function(c, ss){
5657                 var is = Roo.DomQuery.is;
5658                 var r = [], ri = -1;
5659                 for(var i = 0, ci; ci = c[i]; i++){
5660                     var n = next(ci);
5661                     if(n && is(n, ss)){
5662                         r[++ri] = ci;
5663                     }
5664                 }
5665                 return r;
5666             },
5667
5668             "prev" : function(c, ss){
5669                 var is = Roo.DomQuery.is;
5670                 var r = [], ri = -1;
5671                 for(var i = 0, ci; ci = c[i]; i++){
5672                     var n = prev(ci);
5673                     if(n && is(n, ss)){
5674                         r[++ri] = ci;
5675                     }
5676                 }
5677                 return r;
5678             }
5679         }
5680     };
5681 }();
5682
5683 /**
5684  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5685  * @param {String} path The selector/xpath query
5686  * @param {Node} root (optional) The start of the query (defaults to document).
5687  * @return {Array}
5688  * @member Roo
5689  * @method query
5690  */
5691 Roo.query = Roo.DomQuery.select;
5692 /*
5693  * Based on:
5694  * Ext JS Library 1.1.1
5695  * Copyright(c) 2006-2007, Ext JS, LLC.
5696  *
5697  * Originally Released Under LGPL - original licence link has changed is not relivant.
5698  *
5699  * Fork - LGPL
5700  * <script type="text/javascript">
5701  */
5702
5703 /**
5704  * @class Roo.util.Observable
5705  * Base class that provides a common interface for publishing events. Subclasses are expected to
5706  * to have a property "events" with all the events defined.<br>
5707  * For example:
5708  * <pre><code>
5709  Employee = function(name){
5710     this.name = name;
5711     this.addEvents({
5712         "fired" : true,
5713         "quit" : true
5714     });
5715  }
5716  Roo.extend(Employee, Roo.util.Observable);
5717 </code></pre>
5718  * @param {Object} config properties to use (incuding events / listeners)
5719  */
5720
5721 Roo.util.Observable = function(cfg){
5722     
5723     cfg = cfg|| {};
5724     this.addEvents(cfg.events || {});
5725     if (cfg.events) {
5726         delete cfg.events; // make sure
5727     }
5728      
5729     Roo.apply(this, cfg);
5730     
5731     if(this.listeners){
5732         this.on(this.listeners);
5733         delete this.listeners;
5734     }
5735 };
5736 Roo.util.Observable.prototype = {
5737     /** 
5738  * @cfg {Object} listeners  list of events and functions to call for this object, 
5739  * For example :
5740  * <pre><code>
5741     listeners :  { 
5742        'click' : function(e) {
5743            ..... 
5744         } ,
5745         .... 
5746     } 
5747   </code></pre>
5748  */
5749     
5750     
5751     /**
5752      * Fires the specified event with the passed parameters (minus the event name).
5753      * @param {String} eventName
5754      * @param {Object...} args Variable number of parameters are passed to handlers
5755      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5756      */
5757     fireEvent : function(){
5758         var ce = this.events[arguments[0].toLowerCase()];
5759         if(typeof ce == "object"){
5760             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5761         }else{
5762             return true;
5763         }
5764     },
5765
5766     // private
5767     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5768
5769     /**
5770      * Appends an event handler to this component
5771      * @param {String}   eventName The type of event to listen for
5772      * @param {Function} handler The method the event invokes
5773      * @param {Object}   scope (optional) The scope in which to execute the handler
5774      * function. The handler function's "this" context.
5775      * @param {Object}   options (optional) An object containing handler configuration
5776      * properties. This may contain any of the following properties:<ul>
5777      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5778      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5779      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5780      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5781      * by the specified number of milliseconds. If the event fires again within that time, the original
5782      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5783      * </ul><br>
5784      * <p>
5785      * <b>Combining Options</b><br>
5786      * Using the options argument, it is possible to combine different types of listeners:<br>
5787      * <br>
5788      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5789                 <pre><code>
5790                 el.on('click', this.onClick, this, {
5791                         single: true,
5792                 delay: 100,
5793                 forumId: 4
5794                 });
5795                 </code></pre>
5796      * <p>
5797      * <b>Attaching multiple handlers in 1 call</b><br>
5798      * The method also allows for a single argument to be passed which is a config object containing properties
5799      * which specify multiple handlers.
5800      * <pre><code>
5801                 el.on({
5802                         'click': {
5803                         fn: this.onClick,
5804                         scope: this,
5805                         delay: 100
5806                 }, 
5807                 'mouseover': {
5808                         fn: this.onMouseOver,
5809                         scope: this
5810                 },
5811                 'mouseout': {
5812                         fn: this.onMouseOut,
5813                         scope: this
5814                 }
5815                 });
5816                 </code></pre>
5817      * <p>
5818      * Or a shorthand syntax which passes the same scope object to all handlers:
5819         <pre><code>
5820                 el.on({
5821                         'click': this.onClick,
5822                 'mouseover': this.onMouseOver,
5823                 'mouseout': this.onMouseOut,
5824                 scope: this
5825                 });
5826                 </code></pre>
5827      */
5828     addListener : function(eventName, fn, scope, o){
5829         if(typeof eventName == "object"){
5830             o = eventName;
5831             for(var e in o){
5832                 if(this.filterOptRe.test(e)){
5833                     continue;
5834                 }
5835                 if(typeof o[e] == "function"){
5836                     // shared options
5837                     this.addListener(e, o[e], o.scope,  o);
5838                 }else{
5839                     // individual options
5840                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5841                 }
5842             }
5843             return;
5844         }
5845         o = (!o || typeof o == "boolean") ? {} : o;
5846         eventName = eventName.toLowerCase();
5847         var ce = this.events[eventName] || true;
5848         if(typeof ce == "boolean"){
5849             ce = new Roo.util.Event(this, eventName);
5850             this.events[eventName] = ce;
5851         }
5852         ce.addListener(fn, scope, o);
5853     },
5854
5855     /**
5856      * Removes a listener
5857      * @param {String}   eventName     The type of event to listen for
5858      * @param {Function} handler        The handler to remove
5859      * @param {Object}   scope  (optional) The scope (this object) for the handler
5860      */
5861     removeListener : function(eventName, fn, scope){
5862         var ce = this.events[eventName.toLowerCase()];
5863         if(typeof ce == "object"){
5864             ce.removeListener(fn, scope);
5865         }
5866     },
5867
5868     /**
5869      * Removes all listeners for this object
5870      */
5871     purgeListeners : function(){
5872         for(var evt in this.events){
5873             if(typeof this.events[evt] == "object"){
5874                  this.events[evt].clearListeners();
5875             }
5876         }
5877     },
5878
5879     relayEvents : function(o, events){
5880         var createHandler = function(ename){
5881             return function(){
5882                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5883             };
5884         };
5885         for(var i = 0, len = events.length; i < len; i++){
5886             var ename = events[i];
5887             if(!this.events[ename]){ this.events[ename] = true; };
5888             o.on(ename, createHandler(ename), this);
5889         }
5890     },
5891
5892     /**
5893      * Used to define events on this Observable
5894      * @param {Object} object The object with the events defined
5895      */
5896     addEvents : function(o){
5897         if(!this.events){
5898             this.events = {};
5899         }
5900         Roo.applyIf(this.events, o);
5901     },
5902
5903     /**
5904      * Checks to see if this object has any listeners for a specified event
5905      * @param {String} eventName The name of the event to check for
5906      * @return {Boolean} True if the event is being listened for, else false
5907      */
5908     hasListener : function(eventName){
5909         var e = this.events[eventName];
5910         return typeof e == "object" && e.listeners.length > 0;
5911     }
5912 };
5913 /**
5914  * Appends an event handler to this element (shorthand for addListener)
5915  * @param {String}   eventName     The type of event to listen for
5916  * @param {Function} handler        The method the event invokes
5917  * @param {Object}   scope (optional) The scope in which to execute the handler
5918  * function. The handler function's "this" context.
5919  * @param {Object}   options  (optional)
5920  * @method
5921  */
5922 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5923 /**
5924  * Removes a listener (shorthand for removeListener)
5925  * @param {String}   eventName     The type of event to listen for
5926  * @param {Function} handler        The handler to remove
5927  * @param {Object}   scope  (optional) The scope (this object) for the handler
5928  * @method
5929  */
5930 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5931
5932 /**
5933  * Starts capture on the specified Observable. All events will be passed
5934  * to the supplied function with the event name + standard signature of the event
5935  * <b>before</b> the event is fired. If the supplied function returns false,
5936  * the event will not fire.
5937  * @param {Observable} o The Observable to capture
5938  * @param {Function} fn The function to call
5939  * @param {Object} scope (optional) The scope (this object) for the fn
5940  * @static
5941  */
5942 Roo.util.Observable.capture = function(o, fn, scope){
5943     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5944 };
5945
5946 /**
5947  * Removes <b>all</b> added captures from the Observable.
5948  * @param {Observable} o The Observable to release
5949  * @static
5950  */
5951 Roo.util.Observable.releaseCapture = function(o){
5952     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5953 };
5954
5955 (function(){
5956
5957     var createBuffered = function(h, o, scope){
5958         var task = new Roo.util.DelayedTask();
5959         return function(){
5960             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5961         };
5962     };
5963
5964     var createSingle = function(h, e, fn, scope){
5965         return function(){
5966             e.removeListener(fn, scope);
5967             return h.apply(scope, arguments);
5968         };
5969     };
5970
5971     var createDelayed = function(h, o, scope){
5972         return function(){
5973             var args = Array.prototype.slice.call(arguments, 0);
5974             setTimeout(function(){
5975                 h.apply(scope, args);
5976             }, o.delay || 10);
5977         };
5978     };
5979
5980     Roo.util.Event = function(obj, name){
5981         this.name = name;
5982         this.obj = obj;
5983         this.listeners = [];
5984     };
5985
5986     Roo.util.Event.prototype = {
5987         addListener : function(fn, scope, options){
5988             var o = options || {};
5989             scope = scope || this.obj;
5990             if(!this.isListening(fn, scope)){
5991                 var l = {fn: fn, scope: scope, options: o};
5992                 var h = fn;
5993                 if(o.delay){
5994                     h = createDelayed(h, o, scope);
5995                 }
5996                 if(o.single){
5997                     h = createSingle(h, this, fn, scope);
5998                 }
5999                 if(o.buffer){
6000                     h = createBuffered(h, o, scope);
6001                 }
6002                 l.fireFn = h;
6003                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6004                     this.listeners.push(l);
6005                 }else{
6006                     this.listeners = this.listeners.slice(0);
6007                     this.listeners.push(l);
6008                 }
6009             }
6010         },
6011
6012         findListener : function(fn, scope){
6013             scope = scope || this.obj;
6014             var ls = this.listeners;
6015             for(var i = 0, len = ls.length; i < len; i++){
6016                 var l = ls[i];
6017                 if(l.fn == fn && l.scope == scope){
6018                     return i;
6019                 }
6020             }
6021             return -1;
6022         },
6023
6024         isListening : function(fn, scope){
6025             return this.findListener(fn, scope) != -1;
6026         },
6027
6028         removeListener : function(fn, scope){
6029             var index;
6030             if((index = this.findListener(fn, scope)) != -1){
6031                 if(!this.firing){
6032                     this.listeners.splice(index, 1);
6033                 }else{
6034                     this.listeners = this.listeners.slice(0);
6035                     this.listeners.splice(index, 1);
6036                 }
6037                 return true;
6038             }
6039             return false;
6040         },
6041
6042         clearListeners : function(){
6043             this.listeners = [];
6044         },
6045
6046         fire : function(){
6047             var ls = this.listeners, scope, len = ls.length;
6048             if(len > 0){
6049                 this.firing = true;
6050                 var args = Array.prototype.slice.call(arguments, 0);
6051                 for(var i = 0; i < len; i++){
6052                     var l = ls[i];
6053                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6054                         this.firing = false;
6055                         return false;
6056                     }
6057                 }
6058                 this.firing = false;
6059             }
6060             return true;
6061         }
6062     };
6063 })();/*
6064  * RooJS Library 
6065  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6066  *
6067  * Licence LGPL 
6068  *
6069  */
6070  
6071 /**
6072  * @class Roo.Document
6073  * @extends Roo.util.Observable
6074  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6075  * 
6076  * @param {Object} config the methods and properties of the 'base' class for the application.
6077  * 
6078  *  Generic Page handler - implement this to start your app..
6079  * 
6080  * eg.
6081  *  MyProject = new Roo.Document({
6082         events : {
6083             'load' : true // your events..
6084         },
6085         listeners : {
6086             'ready' : function() {
6087                 // fired on Roo.onReady()
6088             }
6089         }
6090  * 
6091  */
6092 Roo.Document = function(cfg) {
6093      
6094     this.addEvents({ 
6095         'ready' : true
6096     });
6097     Roo.util.Observable.call(this,cfg);
6098     
6099     var _this = this;
6100     
6101     Roo.onReady(function() {
6102         _this.fireEvent('ready');
6103     },null,false);
6104     
6105     
6106 }
6107
6108 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6109  * Based on:
6110  * Ext JS Library 1.1.1
6111  * Copyright(c) 2006-2007, Ext JS, LLC.
6112  *
6113  * Originally Released Under LGPL - original licence link has changed is not relivant.
6114  *
6115  * Fork - LGPL
6116  * <script type="text/javascript">
6117  */
6118
6119 /**
6120  * @class Roo.EventManager
6121  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6122  * several useful events directly.
6123  * See {@link Roo.EventObject} for more details on normalized event objects.
6124  * @singleton
6125  */
6126 Roo.EventManager = function(){
6127     var docReadyEvent, docReadyProcId, docReadyState = false;
6128     var resizeEvent, resizeTask, textEvent, textSize;
6129     var E = Roo.lib.Event;
6130     var D = Roo.lib.Dom;
6131
6132     
6133     
6134
6135     var fireDocReady = function(){
6136         if(!docReadyState){
6137             docReadyState = true;
6138             Roo.isReady = true;
6139             if(docReadyProcId){
6140                 clearInterval(docReadyProcId);
6141             }
6142             if(Roo.isGecko || Roo.isOpera) {
6143                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6144             }
6145             if(Roo.isIE){
6146                 var defer = document.getElementById("ie-deferred-loader");
6147                 if(defer){
6148                     defer.onreadystatechange = null;
6149                     defer.parentNode.removeChild(defer);
6150                 }
6151             }
6152             if(docReadyEvent){
6153                 docReadyEvent.fire();
6154                 docReadyEvent.clearListeners();
6155             }
6156         }
6157     };
6158     
6159     var initDocReady = function(){
6160         docReadyEvent = new Roo.util.Event();
6161         if(Roo.isGecko || Roo.isOpera) {
6162             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6163         }else if(Roo.isIE){
6164             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6165             var defer = document.getElementById("ie-deferred-loader");
6166             defer.onreadystatechange = function(){
6167                 if(this.readyState == "complete"){
6168                     fireDocReady();
6169                 }
6170             };
6171         }else if(Roo.isSafari){ 
6172             docReadyProcId = setInterval(function(){
6173                 var rs = document.readyState;
6174                 if(rs == "complete") {
6175                     fireDocReady();     
6176                  }
6177             }, 10);
6178         }
6179         // no matter what, make sure it fires on load
6180         E.on(window, "load", fireDocReady);
6181     };
6182
6183     var createBuffered = function(h, o){
6184         var task = new Roo.util.DelayedTask(h);
6185         return function(e){
6186             // create new event object impl so new events don't wipe out properties
6187             e = new Roo.EventObjectImpl(e);
6188             task.delay(o.buffer, h, null, [e]);
6189         };
6190     };
6191
6192     var createSingle = function(h, el, ename, fn){
6193         return function(e){
6194             Roo.EventManager.removeListener(el, ename, fn);
6195             h(e);
6196         };
6197     };
6198
6199     var createDelayed = function(h, o){
6200         return function(e){
6201             // create new event object impl so new events don't wipe out properties
6202             e = new Roo.EventObjectImpl(e);
6203             setTimeout(function(){
6204                 h(e);
6205             }, o.delay || 10);
6206         };
6207     };
6208     var transitionEndVal = false;
6209     
6210     var transitionEnd = function()
6211     {
6212         if (transitionEndVal) {
6213             return transitionEndVal;
6214         }
6215         var el = document.createElement('div');
6216
6217         var transEndEventNames = {
6218             WebkitTransition : 'webkitTransitionEnd',
6219             MozTransition    : 'transitionend',
6220             OTransition      : 'oTransitionEnd otransitionend',
6221             transition       : 'transitionend'
6222         };
6223     
6224         for (var name in transEndEventNames) {
6225             if (el.style[name] !== undefined) {
6226                 transitionEndVal = transEndEventNames[name];
6227                 return  transitionEndVal ;
6228             }
6229         }
6230     }
6231     
6232
6233     var listen = function(element, ename, opt, fn, scope){
6234         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6235         fn = fn || o.fn; scope = scope || o.scope;
6236         var el = Roo.getDom(element);
6237         
6238         
6239         if(!el){
6240             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6241         }
6242         
6243         if (ename == 'transitionend') {
6244             ename = transitionEnd();
6245         }
6246         var h = function(e){
6247             e = Roo.EventObject.setEvent(e);
6248             var t;
6249             if(o.delegate){
6250                 t = e.getTarget(o.delegate, el);
6251                 if(!t){
6252                     return;
6253                 }
6254             }else{
6255                 t = e.target;
6256             }
6257             if(o.stopEvent === true){
6258                 e.stopEvent();
6259             }
6260             if(o.preventDefault === true){
6261                e.preventDefault();
6262             }
6263             if(o.stopPropagation === true){
6264                 e.stopPropagation();
6265             }
6266
6267             if(o.normalized === false){
6268                 e = e.browserEvent;
6269             }
6270
6271             fn.call(scope || el, e, t, o);
6272         };
6273         if(o.delay){
6274             h = createDelayed(h, o);
6275         }
6276         if(o.single){
6277             h = createSingle(h, el, ename, fn);
6278         }
6279         if(o.buffer){
6280             h = createBuffered(h, o);
6281         }
6282         fn._handlers = fn._handlers || [];
6283         
6284         
6285         fn._handlers.push([Roo.id(el), ename, h]);
6286         
6287         
6288          
6289         E.on(el, ename, h);
6290         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6291             el.addEventListener("DOMMouseScroll", h, false);
6292             E.on(window, 'unload', function(){
6293                 el.removeEventListener("DOMMouseScroll", h, false);
6294             });
6295         }
6296         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6297             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6298         }
6299         return h;
6300     };
6301
6302     var stopListening = function(el, ename, fn){
6303         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6304         if(hds){
6305             for(var i = 0, len = hds.length; i < len; i++){
6306                 var h = hds[i];
6307                 if(h[0] == id && h[1] == ename){
6308                     hd = h[2];
6309                     hds.splice(i, 1);
6310                     break;
6311                 }
6312             }
6313         }
6314         E.un(el, ename, hd);
6315         el = Roo.getDom(el);
6316         if(ename == "mousewheel" && el.addEventListener){
6317             el.removeEventListener("DOMMouseScroll", hd, false);
6318         }
6319         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6320             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6321         }
6322     };
6323
6324     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6325     
6326     var pub = {
6327         
6328         
6329         /** 
6330          * Fix for doc tools
6331          * @scope Roo.EventManager
6332          */
6333         
6334         
6335         /** 
6336          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6337          * object with a Roo.EventObject
6338          * @param {Function} fn        The method the event invokes
6339          * @param {Object}   scope    An object that becomes the scope of the handler
6340          * @param {boolean}  override If true, the obj passed in becomes
6341          *                             the execution scope of the listener
6342          * @return {Function} The wrapped function
6343          * @deprecated
6344          */
6345         wrap : function(fn, scope, override){
6346             return function(e){
6347                 Roo.EventObject.setEvent(e);
6348                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6349             };
6350         },
6351         
6352         /**
6353      * Appends an event handler to an element (shorthand for addListener)
6354      * @param {String/HTMLElement}   element        The html element or id to assign the
6355      * @param {String}   eventName The type of event to listen for
6356      * @param {Function} handler The method the event invokes
6357      * @param {Object}   scope (optional) The scope in which to execute the handler
6358      * function. The handler function's "this" context.
6359      * @param {Object}   options (optional) An object containing handler configuration
6360      * properties. This may contain any of the following properties:<ul>
6361      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6362      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6363      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6364      * <li>preventDefault {Boolean} True to prevent the default action</li>
6365      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6366      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6367      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6368      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6369      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6370      * by the specified number of milliseconds. If the event fires again within that time, the original
6371      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6372      * </ul><br>
6373      * <p>
6374      * <b>Combining Options</b><br>
6375      * Using the options argument, it is possible to combine different types of listeners:<br>
6376      * <br>
6377      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6378      * Code:<pre><code>
6379 el.on('click', this.onClick, this, {
6380     single: true,
6381     delay: 100,
6382     stopEvent : true,
6383     forumId: 4
6384 });</code></pre>
6385      * <p>
6386      * <b>Attaching multiple handlers in 1 call</b><br>
6387       * The method also allows for a single argument to be passed which is a config object containing properties
6388      * which specify multiple handlers.
6389      * <p>
6390      * Code:<pre><code>
6391 el.on({
6392     'click' : {
6393         fn: this.onClick
6394         scope: this,
6395         delay: 100
6396     },
6397     'mouseover' : {
6398         fn: this.onMouseOver
6399         scope: this
6400     },
6401     'mouseout' : {
6402         fn: this.onMouseOut
6403         scope: this
6404     }
6405 });</code></pre>
6406      * <p>
6407      * Or a shorthand syntax:<br>
6408      * Code:<pre><code>
6409 el.on({
6410     'click' : this.onClick,
6411     'mouseover' : this.onMouseOver,
6412     'mouseout' : this.onMouseOut
6413     scope: this
6414 });</code></pre>
6415      */
6416         addListener : function(element, eventName, fn, scope, options){
6417             if(typeof eventName == "object"){
6418                 var o = eventName;
6419                 for(var e in o){
6420                     if(propRe.test(e)){
6421                         continue;
6422                     }
6423                     if(typeof o[e] == "function"){
6424                         // shared options
6425                         listen(element, e, o, o[e], o.scope);
6426                     }else{
6427                         // individual options
6428                         listen(element, e, o[e]);
6429                     }
6430                 }
6431                 return;
6432             }
6433             return listen(element, eventName, options, fn, scope);
6434         },
6435         
6436         /**
6437          * Removes an event handler
6438          *
6439          * @param {String/HTMLElement}   element        The id or html element to remove the 
6440          *                             event from
6441          * @param {String}   eventName     The type of event
6442          * @param {Function} fn
6443          * @return {Boolean} True if a listener was actually removed
6444          */
6445         removeListener : function(element, eventName, fn){
6446             return stopListening(element, eventName, fn);
6447         },
6448         
6449         /**
6450          * Fires when the document is ready (before onload and before images are loaded). Can be 
6451          * accessed shorthanded Roo.onReady().
6452          * @param {Function} fn        The method the event invokes
6453          * @param {Object}   scope    An  object that becomes the scope of the handler
6454          * @param {boolean}  options
6455          */
6456         onDocumentReady : function(fn, scope, options){
6457             if(docReadyState){ // if it already fired
6458                 docReadyEvent.addListener(fn, scope, options);
6459                 docReadyEvent.fire();
6460                 docReadyEvent.clearListeners();
6461                 return;
6462             }
6463             if(!docReadyEvent){
6464                 initDocReady();
6465             }
6466             docReadyEvent.addListener(fn, scope, options);
6467         },
6468         
6469         /**
6470          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6471          * @param {Function} fn        The method the event invokes
6472          * @param {Object}   scope    An object that becomes the scope of the handler
6473          * @param {boolean}  options
6474          */
6475         onWindowResize : function(fn, scope, options){
6476             if(!resizeEvent){
6477                 resizeEvent = new Roo.util.Event();
6478                 resizeTask = new Roo.util.DelayedTask(function(){
6479                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                 });
6481                 E.on(window, "resize", function(){
6482                     if(Roo.isIE){
6483                         resizeTask.delay(50);
6484                     }else{
6485                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6486                     }
6487                 });
6488             }
6489             resizeEvent.addListener(fn, scope, options);
6490         },
6491
6492         /**
6493          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6494          * @param {Function} fn        The method the event invokes
6495          * @param {Object}   scope    An object that becomes the scope of the handler
6496          * @param {boolean}  options
6497          */
6498         onTextResize : function(fn, scope, options){
6499             if(!textEvent){
6500                 textEvent = new Roo.util.Event();
6501                 var textEl = new Roo.Element(document.createElement('div'));
6502                 textEl.dom.className = 'x-text-resize';
6503                 textEl.dom.innerHTML = 'X';
6504                 textEl.appendTo(document.body);
6505                 textSize = textEl.dom.offsetHeight;
6506                 setInterval(function(){
6507                     if(textEl.dom.offsetHeight != textSize){
6508                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6509                     }
6510                 }, this.textResizeInterval);
6511             }
6512             textEvent.addListener(fn, scope, options);
6513         },
6514
6515         /**
6516          * Removes the passed window resize listener.
6517          * @param {Function} fn        The method the event invokes
6518          * @param {Object}   scope    The scope of handler
6519          */
6520         removeResizeListener : function(fn, scope){
6521             if(resizeEvent){
6522                 resizeEvent.removeListener(fn, scope);
6523             }
6524         },
6525
6526         // private
6527         fireResize : function(){
6528             if(resizeEvent){
6529                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6530             }   
6531         },
6532         /**
6533          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6534          */
6535         ieDeferSrc : false,
6536         /**
6537          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6538          */
6539         textResizeInterval : 50
6540     };
6541     
6542     /**
6543      * Fix for doc tools
6544      * @scopeAlias pub=Roo.EventManager
6545      */
6546     
6547      /**
6548      * Appends an event handler to an element (shorthand for addListener)
6549      * @param {String/HTMLElement}   element        The html element or id to assign the
6550      * @param {String}   eventName The type of event to listen for
6551      * @param {Function} handler The method the event invokes
6552      * @param {Object}   scope (optional) The scope in which to execute the handler
6553      * function. The handler function's "this" context.
6554      * @param {Object}   options (optional) An object containing handler configuration
6555      * properties. This may contain any of the following properties:<ul>
6556      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6557      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6558      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6559      * <li>preventDefault {Boolean} True to prevent the default action</li>
6560      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6561      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6562      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6563      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6564      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6565      * by the specified number of milliseconds. If the event fires again within that time, the original
6566      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6567      * </ul><br>
6568      * <p>
6569      * <b>Combining Options</b><br>
6570      * Using the options argument, it is possible to combine different types of listeners:<br>
6571      * <br>
6572      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6573      * Code:<pre><code>
6574 el.on('click', this.onClick, this, {
6575     single: true,
6576     delay: 100,
6577     stopEvent : true,
6578     forumId: 4
6579 });</code></pre>
6580      * <p>
6581      * <b>Attaching multiple handlers in 1 call</b><br>
6582       * The method also allows for a single argument to be passed which is a config object containing properties
6583      * which specify multiple handlers.
6584      * <p>
6585      * Code:<pre><code>
6586 el.on({
6587     'click' : {
6588         fn: this.onClick
6589         scope: this,
6590         delay: 100
6591     },
6592     'mouseover' : {
6593         fn: this.onMouseOver
6594         scope: this
6595     },
6596     'mouseout' : {
6597         fn: this.onMouseOut
6598         scope: this
6599     }
6600 });</code></pre>
6601      * <p>
6602      * Or a shorthand syntax:<br>
6603      * Code:<pre><code>
6604 el.on({
6605     'click' : this.onClick,
6606     'mouseover' : this.onMouseOver,
6607     'mouseout' : this.onMouseOut
6608     scope: this
6609 });</code></pre>
6610      */
6611     pub.on = pub.addListener;
6612     pub.un = pub.removeListener;
6613
6614     pub.stoppedMouseDownEvent = new Roo.util.Event();
6615     return pub;
6616 }();
6617 /**
6618   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6619   * @param {Function} fn        The method the event invokes
6620   * @param {Object}   scope    An  object that becomes the scope of the handler
6621   * @param {boolean}  override If true, the obj passed in becomes
6622   *                             the execution scope of the listener
6623   * @member Roo
6624   * @method onReady
6625  */
6626 Roo.onReady = Roo.EventManager.onDocumentReady;
6627
6628 Roo.onReady(function(){
6629     var bd = Roo.get(document.body);
6630     if(!bd){ return; }
6631
6632     var cls = [
6633             Roo.isIE ? "roo-ie"
6634             : Roo.isIE11 ? "roo-ie11"
6635             : Roo.isEdge ? "roo-edge"
6636             : Roo.isGecko ? "roo-gecko"
6637             : Roo.isOpera ? "roo-opera"
6638             : Roo.isSafari ? "roo-safari" : ""];
6639
6640     if(Roo.isMac){
6641         cls.push("roo-mac");
6642     }
6643     if(Roo.isLinux){
6644         cls.push("roo-linux");
6645     }
6646     if(Roo.isIOS){
6647         cls.push("roo-ios");
6648     }
6649     if(Roo.isTouch){
6650         cls.push("roo-touch");
6651     }
6652     if(Roo.isBorderBox){
6653         cls.push('roo-border-box');
6654     }
6655     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6656         var p = bd.dom.parentNode;
6657         if(p){
6658             p.className += ' roo-strict';
6659         }
6660     }
6661     bd.addClass(cls.join(' '));
6662 });
6663
6664 /**
6665  * @class Roo.EventObject
6666  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6667  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6668  * Example:
6669  * <pre><code>
6670  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6671     e.preventDefault();
6672     var target = e.getTarget();
6673     ...
6674  }
6675  var myDiv = Roo.get("myDiv");
6676  myDiv.on("click", handleClick);
6677  //or
6678  Roo.EventManager.on("myDiv", 'click', handleClick);
6679  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6680  </code></pre>
6681  * @singleton
6682  */
6683 Roo.EventObject = function(){
6684     
6685     var E = Roo.lib.Event;
6686     
6687     // safari keypress events for special keys return bad keycodes
6688     var safariKeys = {
6689         63234 : 37, // left
6690         63235 : 39, // right
6691         63232 : 38, // up
6692         63233 : 40, // down
6693         63276 : 33, // page up
6694         63277 : 34, // page down
6695         63272 : 46, // delete
6696         63273 : 36, // home
6697         63275 : 35  // end
6698     };
6699
6700     // normalize button clicks
6701     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6702                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6703
6704     Roo.EventObjectImpl = function(e){
6705         if(e){
6706             this.setEvent(e.browserEvent || e);
6707         }
6708     };
6709     Roo.EventObjectImpl.prototype = {
6710         /**
6711          * Used to fix doc tools.
6712          * @scope Roo.EventObject.prototype
6713          */
6714             
6715
6716         
6717         
6718         /** The normal browser event */
6719         browserEvent : null,
6720         /** The button pressed in a mouse event */
6721         button : -1,
6722         /** True if the shift key was down during the event */
6723         shiftKey : false,
6724         /** True if the control key was down during the event */
6725         ctrlKey : false,
6726         /** True if the alt key was down during the event */
6727         altKey : false,
6728
6729         /** Key constant 
6730         * @type Number */
6731         BACKSPACE : 8,
6732         /** Key constant 
6733         * @type Number */
6734         TAB : 9,
6735         /** Key constant 
6736         * @type Number */
6737         RETURN : 13,
6738         /** Key constant 
6739         * @type Number */
6740         ENTER : 13,
6741         /** Key constant 
6742         * @type Number */
6743         SHIFT : 16,
6744         /** Key constant 
6745         * @type Number */
6746         CONTROL : 17,
6747         /** Key constant 
6748         * @type Number */
6749         ESC : 27,
6750         /** Key constant 
6751         * @type Number */
6752         SPACE : 32,
6753         /** Key constant 
6754         * @type Number */
6755         PAGEUP : 33,
6756         /** Key constant 
6757         * @type Number */
6758         PAGEDOWN : 34,
6759         /** Key constant 
6760         * @type Number */
6761         END : 35,
6762         /** Key constant 
6763         * @type Number */
6764         HOME : 36,
6765         /** Key constant 
6766         * @type Number */
6767         LEFT : 37,
6768         /** Key constant 
6769         * @type Number */
6770         UP : 38,
6771         /** Key constant 
6772         * @type Number */
6773         RIGHT : 39,
6774         /** Key constant 
6775         * @type Number */
6776         DOWN : 40,
6777         /** Key constant 
6778         * @type Number */
6779         DELETE : 46,
6780         /** Key constant 
6781         * @type Number */
6782         F5 : 116,
6783
6784            /** @private */
6785         setEvent : function(e){
6786             if(e == this || (e && e.browserEvent)){ // already wrapped
6787                 return e;
6788             }
6789             this.browserEvent = e;
6790             if(e){
6791                 // normalize buttons
6792                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6793                 if(e.type == 'click' && this.button == -1){
6794                     this.button = 0;
6795                 }
6796                 this.type = e.type;
6797                 this.shiftKey = e.shiftKey;
6798                 // mac metaKey behaves like ctrlKey
6799                 this.ctrlKey = e.ctrlKey || e.metaKey;
6800                 this.altKey = e.altKey;
6801                 // in getKey these will be normalized for the mac
6802                 this.keyCode = e.keyCode;
6803                 // keyup warnings on firefox.
6804                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6805                 // cache the target for the delayed and or buffered events
6806                 this.target = E.getTarget(e);
6807                 // same for XY
6808                 this.xy = E.getXY(e);
6809             }else{
6810                 this.button = -1;
6811                 this.shiftKey = false;
6812                 this.ctrlKey = false;
6813                 this.altKey = false;
6814                 this.keyCode = 0;
6815                 this.charCode =0;
6816                 this.target = null;
6817                 this.xy = [0, 0];
6818             }
6819             return this;
6820         },
6821
6822         /**
6823          * Stop the event (preventDefault and stopPropagation)
6824          */
6825         stopEvent : function(){
6826             if(this.browserEvent){
6827                 if(this.browserEvent.type == 'mousedown'){
6828                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6829                 }
6830                 E.stopEvent(this.browserEvent);
6831             }
6832         },
6833
6834         /**
6835          * Prevents the browsers default handling of the event.
6836          */
6837         preventDefault : function(){
6838             if(this.browserEvent){
6839                 E.preventDefault(this.browserEvent);
6840             }
6841         },
6842
6843         /** @private */
6844         isNavKeyPress : function(){
6845             var k = this.keyCode;
6846             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6847             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6848         },
6849
6850         isSpecialKey : function(){
6851             var k = this.keyCode;
6852             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6853             (k == 16) || (k == 17) ||
6854             (k >= 18 && k <= 20) ||
6855             (k >= 33 && k <= 35) ||
6856             (k >= 36 && k <= 39) ||
6857             (k >= 44 && k <= 45);
6858         },
6859         /**
6860          * Cancels bubbling of the event.
6861          */
6862         stopPropagation : function(){
6863             if(this.browserEvent){
6864                 if(this.type == 'mousedown'){
6865                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6866                 }
6867                 E.stopPropagation(this.browserEvent);
6868             }
6869         },
6870
6871         /**
6872          * Gets the key code for the event.
6873          * @return {Number}
6874          */
6875         getCharCode : function(){
6876             return this.charCode || this.keyCode;
6877         },
6878
6879         /**
6880          * Returns a normalized keyCode for the event.
6881          * @return {Number} The key code
6882          */
6883         getKey : function(){
6884             var k = this.keyCode || this.charCode;
6885             return Roo.isSafari ? (safariKeys[k] || k) : k;
6886         },
6887
6888         /**
6889          * Gets the x coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageX : function(){
6893             return this.xy[0];
6894         },
6895
6896         /**
6897          * Gets the y coordinate of the event.
6898          * @return {Number}
6899          */
6900         getPageY : function(){
6901             return this.xy[1];
6902         },
6903
6904         /**
6905          * Gets the time of the event.
6906          * @return {Number}
6907          */
6908         getTime : function(){
6909             if(this.browserEvent){
6910                 return E.getTime(this.browserEvent);
6911             }
6912             return null;
6913         },
6914
6915         /**
6916          * Gets the page coordinates of the event.
6917          * @return {Array} The xy values like [x, y]
6918          */
6919         getXY : function(){
6920             return this.xy;
6921         },
6922
6923         /**
6924          * Gets the target for the event.
6925          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6926          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6927                 search as a number or element (defaults to 10 || document.body)
6928          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6929          * @return {HTMLelement}
6930          */
6931         getTarget : function(selector, maxDepth, returnEl){
6932             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6933         },
6934         /**
6935          * Gets the related target.
6936          * @return {HTMLElement}
6937          */
6938         getRelatedTarget : function(){
6939             if(this.browserEvent){
6940                 return E.getRelatedTarget(this.browserEvent);
6941             }
6942             return null;
6943         },
6944
6945         /**
6946          * Normalizes mouse wheel delta across browsers
6947          * @return {Number} The delta
6948          */
6949         getWheelDelta : function(){
6950             var e = this.browserEvent;
6951             var delta = 0;
6952             if(e.wheelDelta){ /* IE/Opera. */
6953                 delta = e.wheelDelta/120;
6954             }else if(e.detail){ /* Mozilla case. */
6955                 delta = -e.detail/3;
6956             }
6957             return delta;
6958         },
6959
6960         /**
6961          * Returns true if the control, meta, shift or alt key was pressed during this event.
6962          * @return {Boolean}
6963          */
6964         hasModifier : function(){
6965             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6966         },
6967
6968         /**
6969          * Returns true if the target of this event equals el or is a child of el
6970          * @param {String/HTMLElement/Element} el
6971          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6972          * @return {Boolean}
6973          */
6974         within : function(el, related){
6975             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6976             return t && Roo.fly(el).contains(t);
6977         },
6978
6979         getPoint : function(){
6980             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6981         }
6982     };
6983
6984     return new Roo.EventObjectImpl();
6985 }();
6986             
6987     /*
6988  * Based on:
6989  * Ext JS Library 1.1.1
6990  * Copyright(c) 2006-2007, Ext JS, LLC.
6991  *
6992  * Originally Released Under LGPL - original licence link has changed is not relivant.
6993  *
6994  * Fork - LGPL
6995  * <script type="text/javascript">
6996  */
6997
6998  
6999 // was in Composite Element!??!?!
7000  
7001 (function(){
7002     var D = Roo.lib.Dom;
7003     var E = Roo.lib.Event;
7004     var A = Roo.lib.Anim;
7005
7006     // local style camelizing for speed
7007     var propCache = {};
7008     var camelRe = /(-[a-z])/gi;
7009     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7010     var view = document.defaultView;
7011
7012 /**
7013  * @class Roo.Element
7014  * Represents an Element in the DOM.<br><br>
7015  * Usage:<br>
7016 <pre><code>
7017 var el = Roo.get("my-div");
7018
7019 // or with getEl
7020 var el = getEl("my-div");
7021
7022 // or with a DOM element
7023 var el = Roo.get(myDivElement);
7024 </code></pre>
7025  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7026  * each call instead of constructing a new one.<br><br>
7027  * <b>Animations</b><br />
7028  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7029  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7030 <pre>
7031 Option    Default   Description
7032 --------- --------  ---------------------------------------------
7033 duration  .35       The duration of the animation in seconds
7034 easing    easeOut   The YUI easing method
7035 callback  none      A function to execute when the anim completes
7036 scope     this      The scope (this) of the callback function
7037 </pre>
7038 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7039 * manipulate the animation. Here's an example:
7040 <pre><code>
7041 var el = Roo.get("my-div");
7042
7043 // no animation
7044 el.setWidth(100);
7045
7046 // default animation
7047 el.setWidth(100, true);
7048
7049 // animation with some options set
7050 el.setWidth(100, {
7051     duration: 1,
7052     callback: this.foo,
7053     scope: this
7054 });
7055
7056 // using the "anim" property to get the Anim object
7057 var opt = {
7058     duration: 1,
7059     callback: this.foo,
7060     scope: this
7061 };
7062 el.setWidth(100, opt);
7063 ...
7064 if(opt.anim.isAnimated()){
7065     opt.anim.stop();
7066 }
7067 </code></pre>
7068 * <b> Composite (Collections of) Elements</b><br />
7069  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7070  * @constructor Create a new Element directly.
7071  * @param {String/HTMLElement} element
7072  * @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).
7073  */
7074     Roo.Element = function(element, forceNew){
7075         var dom = typeof element == "string" ?
7076                 document.getElementById(element) : element;
7077         if(!dom){ // invalid id/element
7078             return null;
7079         }
7080         var id = dom.id;
7081         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7082             return Roo.Element.cache[id];
7083         }
7084
7085         /**
7086          * The DOM element
7087          * @type HTMLElement
7088          */
7089         this.dom = dom;
7090
7091         /**
7092          * The DOM element ID
7093          * @type String
7094          */
7095         this.id = id || Roo.id(dom);
7096     };
7097
7098     var El = Roo.Element;
7099
7100     El.prototype = {
7101         /**
7102          * The element's default display mode  (defaults to "")
7103          * @type String
7104          */
7105         originalDisplay : "",
7106
7107         visibilityMode : 1,
7108         /**
7109          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7110          * @type String
7111          */
7112         defaultUnit : "px",
7113         
7114         /**
7115          * Sets the element's visibility mode. When setVisible() is called it
7116          * will use this to determine whether to set the visibility or the display property.
7117          * @param visMode Element.VISIBILITY or Element.DISPLAY
7118          * @return {Roo.Element} this
7119          */
7120         setVisibilityMode : function(visMode){
7121             this.visibilityMode = visMode;
7122             return this;
7123         },
7124         /**
7125          * Convenience method for setVisibilityMode(Element.DISPLAY)
7126          * @param {String} display (optional) What to set display to when visible
7127          * @return {Roo.Element} this
7128          */
7129         enableDisplayMode : function(display){
7130             this.setVisibilityMode(El.DISPLAY);
7131             if(typeof display != "undefined") { this.originalDisplay = display; }
7132             return this;
7133         },
7134
7135         /**
7136          * 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)
7137          * @param {String} selector The simple selector to test
7138          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7139                 search as a number or element (defaults to 10 || document.body)
7140          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7141          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7142          */
7143         findParent : function(simpleSelector, maxDepth, returnEl){
7144             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7145             maxDepth = maxDepth || 50;
7146             if(typeof maxDepth != "number"){
7147                 stopEl = Roo.getDom(maxDepth);
7148                 maxDepth = 10;
7149             }
7150             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7151                 if(dq.is(p, simpleSelector)){
7152                     return returnEl ? Roo.get(p) : p;
7153                 }
7154                 depth++;
7155                 p = p.parentNode;
7156             }
7157             return null;
7158         },
7159
7160
7161         /**
7162          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7163          * @param {String} selector The simple selector to test
7164          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7165                 search as a number or element (defaults to 10 || document.body)
7166          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7167          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7168          */
7169         findParentNode : function(simpleSelector, maxDepth, returnEl){
7170             var p = Roo.fly(this.dom.parentNode, '_internal');
7171             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7172         },
7173         
7174         /**
7175          * Looks at  the scrollable parent element
7176          */
7177         findScrollableParent : function()
7178         {
7179             var overflowRegex = /(auto|scroll)/;
7180             
7181             if(this.getStyle('position') === 'fixed'){
7182                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7183             }
7184             
7185             var excludeStaticParent = this.getStyle('position') === "absolute";
7186             
7187             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7188                 
7189                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7190                     continue;
7191                 }
7192                 
7193                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7194                     return parent;
7195                 }
7196                 
7197                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7198                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7199                 }
7200             }
7201             
7202             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7203         },
7204
7205         /**
7206          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7207          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7208          * @param {String} selector The simple selector to test
7209          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7210                 search as a number or element (defaults to 10 || document.body)
7211          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7212          */
7213         up : function(simpleSelector, maxDepth){
7214             return this.findParentNode(simpleSelector, maxDepth, true);
7215         },
7216
7217
7218
7219         /**
7220          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7221          * @param {String} selector The simple selector to test
7222          * @return {Boolean} True if this element matches the selector, else false
7223          */
7224         is : function(simpleSelector){
7225             return Roo.DomQuery.is(this.dom, simpleSelector);
7226         },
7227
7228         /**
7229          * Perform animation on this element.
7230          * @param {Object} args The YUI animation control args
7231          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7232          * @param {Function} onComplete (optional) Function to call when animation completes
7233          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7234          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7235          * @return {Roo.Element} this
7236          */
7237         animate : function(args, duration, onComplete, easing, animType){
7238             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7239             return this;
7240         },
7241
7242         /*
7243          * @private Internal animation call
7244          */
7245         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7246             animType = animType || 'run';
7247             opt = opt || {};
7248             var anim = Roo.lib.Anim[animType](
7249                 this.dom, args,
7250                 (opt.duration || defaultDur) || .35,
7251                 (opt.easing || defaultEase) || 'easeOut',
7252                 function(){
7253                     Roo.callback(cb, this);
7254                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7255                 },
7256                 this
7257             );
7258             opt.anim = anim;
7259             return anim;
7260         },
7261
7262         // private legacy anim prep
7263         preanim : function(a, i){
7264             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7265         },
7266
7267         /**
7268          * Removes worthless text nodes
7269          * @param {Boolean} forceReclean (optional) By default the element
7270          * keeps track if it has been cleaned already so
7271          * you can call this over and over. However, if you update the element and
7272          * need to force a reclean, you can pass true.
7273          */
7274         clean : function(forceReclean){
7275             if(this.isCleaned && forceReclean !== true){
7276                 return this;
7277             }
7278             var ns = /\S/;
7279             var d = this.dom, n = d.firstChild, ni = -1;
7280             while(n){
7281                 var nx = n.nextSibling;
7282                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7283                     d.removeChild(n);
7284                 }else{
7285                     n.nodeIndex = ++ni;
7286                 }
7287                 n = nx;
7288             }
7289             this.isCleaned = true;
7290             return this;
7291         },
7292
7293         // private
7294         calcOffsetsTo : function(el){
7295             el = Roo.get(el);
7296             var d = el.dom;
7297             var restorePos = false;
7298             if(el.getStyle('position') == 'static'){
7299                 el.position('relative');
7300                 restorePos = true;
7301             }
7302             var x = 0, y =0;
7303             var op = this.dom;
7304             while(op && op != d && op.tagName != 'HTML'){
7305                 x+= op.offsetLeft;
7306                 y+= op.offsetTop;
7307                 op = op.offsetParent;
7308             }
7309             if(restorePos){
7310                 el.position('static');
7311             }
7312             return [x, y];
7313         },
7314
7315         /**
7316          * Scrolls this element into view within the passed container.
7317          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7318          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7319          * @return {Roo.Element} this
7320          */
7321         scrollIntoView : function(container, hscroll){
7322             var c = Roo.getDom(container) || document.body;
7323             var el = this.dom;
7324
7325             var o = this.calcOffsetsTo(c),
7326                 l = o[0],
7327                 t = o[1],
7328                 b = t+el.offsetHeight,
7329                 r = l+el.offsetWidth;
7330
7331             var ch = c.clientHeight;
7332             var ct = parseInt(c.scrollTop, 10);
7333             var cl = parseInt(c.scrollLeft, 10);
7334             var cb = ct + ch;
7335             var cr = cl + c.clientWidth;
7336
7337             if(t < ct){
7338                 c.scrollTop = t;
7339             }else if(b > cb){
7340                 c.scrollTop = b-ch;
7341             }
7342
7343             if(hscroll !== false){
7344                 if(l < cl){
7345                     c.scrollLeft = l;
7346                 }else if(r > cr){
7347                     c.scrollLeft = r-c.clientWidth;
7348                 }
7349             }
7350             return this;
7351         },
7352
7353         // private
7354         scrollChildIntoView : function(child, hscroll){
7355             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7356         },
7357
7358         /**
7359          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7360          * the new height may not be available immediately.
7361          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7362          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7363          * @param {Function} onComplete (optional) Function to call when animation completes
7364          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7365          * @return {Roo.Element} this
7366          */
7367         autoHeight : function(animate, duration, onComplete, easing){
7368             var oldHeight = this.getHeight();
7369             this.clip();
7370             this.setHeight(1); // force clipping
7371             setTimeout(function(){
7372                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7373                 if(!animate){
7374                     this.setHeight(height);
7375                     this.unclip();
7376                     if(typeof onComplete == "function"){
7377                         onComplete();
7378                     }
7379                 }else{
7380                     this.setHeight(oldHeight); // restore original height
7381                     this.setHeight(height, animate, duration, function(){
7382                         this.unclip();
7383                         if(typeof onComplete == "function") { onComplete(); }
7384                     }.createDelegate(this), easing);
7385                 }
7386             }.createDelegate(this), 0);
7387             return this;
7388         },
7389
7390         /**
7391          * Returns true if this element is an ancestor of the passed element
7392          * @param {HTMLElement/String} el The element to check
7393          * @return {Boolean} True if this element is an ancestor of el, else false
7394          */
7395         contains : function(el){
7396             if(!el){return false;}
7397             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7398         },
7399
7400         /**
7401          * Checks whether the element is currently visible using both visibility and display properties.
7402          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7403          * @return {Boolean} True if the element is currently visible, else false
7404          */
7405         isVisible : function(deep) {
7406             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7407             if(deep !== true || !vis){
7408                 return vis;
7409             }
7410             var p = this.dom.parentNode;
7411             while(p && p.tagName.toLowerCase() != "body"){
7412                 if(!Roo.fly(p, '_isVisible').isVisible()){
7413                     return false;
7414                 }
7415                 p = p.parentNode;
7416             }
7417             return true;
7418         },
7419
7420         /**
7421          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7422          * @param {String} selector The CSS selector
7423          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7424          * @return {CompositeElement/CompositeElementLite} The composite element
7425          */
7426         select : function(selector, unique){
7427             return El.select(selector, unique, this.dom);
7428         },
7429
7430         /**
7431          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7432          * @param {String} selector The CSS selector
7433          * @return {Array} An array of the matched nodes
7434          */
7435         query : function(selector, unique){
7436             return Roo.DomQuery.select(selector, this.dom);
7437         },
7438
7439         /**
7440          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7441          * @param {String} selector The CSS selector
7442          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7443          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7444          */
7445         child : function(selector, returnDom){
7446             var n = Roo.DomQuery.selectNode(selector, this.dom);
7447             return returnDom ? n : Roo.get(n);
7448         },
7449
7450         /**
7451          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7454          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7455          */
7456         down : function(selector, returnDom){
7457             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7458             return returnDom ? n : Roo.get(n);
7459         },
7460
7461         /**
7462          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7463          * @param {String} group The group the DD object is member of
7464          * @param {Object} config The DD config object
7465          * @param {Object} overrides An object containing methods to override/implement on the DD object
7466          * @return {Roo.dd.DD} The DD object
7467          */
7468         initDD : function(group, config, overrides){
7469             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7470             return Roo.apply(dd, overrides);
7471         },
7472
7473         /**
7474          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7475          * @param {String} group The group the DDProxy object is member of
7476          * @param {Object} config The DDProxy config object
7477          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7478          * @return {Roo.dd.DDProxy} The DDProxy object
7479          */
7480         initDDProxy : function(group, config, overrides){
7481             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7482             return Roo.apply(dd, overrides);
7483         },
7484
7485         /**
7486          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7487          * @param {String} group The group the DDTarget object is member of
7488          * @param {Object} config The DDTarget config object
7489          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7490          * @return {Roo.dd.DDTarget} The DDTarget object
7491          */
7492         initDDTarget : function(group, config, overrides){
7493             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7494             return Roo.apply(dd, overrides);
7495         },
7496
7497         /**
7498          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7499          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7500          * @param {Boolean} visible Whether the element is visible
7501          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7502          * @return {Roo.Element} this
7503          */
7504          setVisible : function(visible, animate){
7505             if(!animate || !A){
7506                 if(this.visibilityMode == El.DISPLAY){
7507                     this.setDisplayed(visible);
7508                 }else{
7509                     this.fixDisplay();
7510                     this.dom.style.visibility = visible ? "visible" : "hidden";
7511                 }
7512             }else{
7513                 // closure for composites
7514                 var dom = this.dom;
7515                 var visMode = this.visibilityMode;
7516                 if(visible){
7517                     this.setOpacity(.01);
7518                     this.setVisible(true);
7519                 }
7520                 this.anim({opacity: { to: (visible?1:0) }},
7521                       this.preanim(arguments, 1),
7522                       null, .35, 'easeIn', function(){
7523                          if(!visible){
7524                              if(visMode == El.DISPLAY){
7525                                  dom.style.display = "none";
7526                              }else{
7527                                  dom.style.visibility = "hidden";
7528                              }
7529                              Roo.get(dom).setOpacity(1);
7530                          }
7531                      });
7532             }
7533             return this;
7534         },
7535
7536         /**
7537          * Returns true if display is not "none"
7538          * @return {Boolean}
7539          */
7540         isDisplayed : function() {
7541             return this.getStyle("display") != "none";
7542         },
7543
7544         /**
7545          * Toggles the element's visibility or display, depending on visibility mode.
7546          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7547          * @return {Roo.Element} this
7548          */
7549         toggle : function(animate){
7550             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7551             return this;
7552         },
7553
7554         /**
7555          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7556          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7557          * @return {Roo.Element} this
7558          */
7559         setDisplayed : function(value) {
7560             if(typeof value == "boolean"){
7561                value = value ? this.originalDisplay : "none";
7562             }
7563             this.setStyle("display", value);
7564             return this;
7565         },
7566
7567         /**
7568          * Tries to focus the element. Any exceptions are caught and ignored.
7569          * @return {Roo.Element} this
7570          */
7571         focus : function() {
7572             try{
7573                 this.dom.focus();
7574             }catch(e){}
7575             return this;
7576         },
7577
7578         /**
7579          * Tries to blur the element. Any exceptions are caught and ignored.
7580          * @return {Roo.Element} this
7581          */
7582         blur : function() {
7583             try{
7584                 this.dom.blur();
7585             }catch(e){}
7586             return this;
7587         },
7588
7589         /**
7590          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7591          * @param {String/Array} className The CSS class to add, or an array of classes
7592          * @return {Roo.Element} this
7593          */
7594         addClass : function(className){
7595             if(className instanceof Array){
7596                 for(var i = 0, len = className.length; i < len; i++) {
7597                     this.addClass(className[i]);
7598                 }
7599             }else{
7600                 if(className && !this.hasClass(className)){
7601                     this.dom.className = this.dom.className + " " + className;
7602                 }
7603             }
7604             return this;
7605         },
7606
7607         /**
7608          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7609          * @param {String/Array} className The CSS class to add, or an array of classes
7610          * @return {Roo.Element} this
7611          */
7612         radioClass : function(className){
7613             var siblings = this.dom.parentNode.childNodes;
7614             for(var i = 0; i < siblings.length; i++) {
7615                 var s = siblings[i];
7616                 if(s.nodeType == 1){
7617                     Roo.get(s).removeClass(className);
7618                 }
7619             }
7620             this.addClass(className);
7621             return this;
7622         },
7623
7624         /**
7625          * Removes one or more CSS classes from the element.
7626          * @param {String/Array} className The CSS class to remove, or an array of classes
7627          * @return {Roo.Element} this
7628          */
7629         removeClass : function(className){
7630             if(!className || !this.dom.className){
7631                 return this;
7632             }
7633             if(className instanceof Array){
7634                 for(var i = 0, len = className.length; i < len; i++) {
7635                     this.removeClass(className[i]);
7636                 }
7637             }else{
7638                 if(this.hasClass(className)){
7639                     var re = this.classReCache[className];
7640                     if (!re) {
7641                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7642                        this.classReCache[className] = re;
7643                     }
7644                     this.dom.className =
7645                         this.dom.className.replace(re, " ");
7646                 }
7647             }
7648             return this;
7649         },
7650
7651         // private
7652         classReCache: {},
7653
7654         /**
7655          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7656          * @param {String} className The CSS class to toggle
7657          * @return {Roo.Element} this
7658          */
7659         toggleClass : function(className){
7660             if(this.hasClass(className)){
7661                 this.removeClass(className);
7662             }else{
7663                 this.addClass(className);
7664             }
7665             return this;
7666         },
7667
7668         /**
7669          * Checks if the specified CSS class exists on this element's DOM node.
7670          * @param {String} className The CSS class to check for
7671          * @return {Boolean} True if the class exists, else false
7672          */
7673         hasClass : function(className){
7674             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7675         },
7676
7677         /**
7678          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7679          * @param {String} oldClassName The CSS class to replace
7680          * @param {String} newClassName The replacement CSS class
7681          * @return {Roo.Element} this
7682          */
7683         replaceClass : function(oldClassName, newClassName){
7684             this.removeClass(oldClassName);
7685             this.addClass(newClassName);
7686             return this;
7687         },
7688
7689         /**
7690          * Returns an object with properties matching the styles requested.
7691          * For example, el.getStyles('color', 'font-size', 'width') might return
7692          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7693          * @param {String} style1 A style name
7694          * @param {String} style2 A style name
7695          * @param {String} etc.
7696          * @return {Object} The style object
7697          */
7698         getStyles : function(){
7699             var a = arguments, len = a.length, r = {};
7700             for(var i = 0; i < len; i++){
7701                 r[a[i]] = this.getStyle(a[i]);
7702             }
7703             return r;
7704         },
7705
7706         /**
7707          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7708          * @param {String} property The style property whose value is returned.
7709          * @return {String} The current value of the style property for this element.
7710          */
7711         getStyle : function(){
7712             return view && view.getComputedStyle ?
7713                 function(prop){
7714                     var el = this.dom, v, cs, camel;
7715                     if(prop == 'float'){
7716                         prop = "cssFloat";
7717                     }
7718                     if(el.style && (v = el.style[prop])){
7719                         return v;
7720                     }
7721                     if(cs = view.getComputedStyle(el, "")){
7722                         if(!(camel = propCache[prop])){
7723                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7724                         }
7725                         return cs[camel];
7726                     }
7727                     return null;
7728                 } :
7729                 function(prop){
7730                     var el = this.dom, v, cs, camel;
7731                     if(prop == 'opacity'){
7732                         if(typeof el.style.filter == 'string'){
7733                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7734                             if(m){
7735                                 var fv = parseFloat(m[1]);
7736                                 if(!isNaN(fv)){
7737                                     return fv ? fv / 100 : 0;
7738                                 }
7739                             }
7740                         }
7741                         return 1;
7742                     }else if(prop == 'float'){
7743                         prop = "styleFloat";
7744                     }
7745                     if(!(camel = propCache[prop])){
7746                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7747                     }
7748                     if(v = el.style[camel]){
7749                         return v;
7750                     }
7751                     if(cs = el.currentStyle){
7752                         return cs[camel];
7753                     }
7754                     return null;
7755                 };
7756         }(),
7757
7758         /**
7759          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7760          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7761          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7762          * @return {Roo.Element} this
7763          */
7764         setStyle : function(prop, value){
7765             if(typeof prop == "string"){
7766                 
7767                 if (prop == 'float') {
7768                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7769                     return this;
7770                 }
7771                 
7772                 var camel;
7773                 if(!(camel = propCache[prop])){
7774                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                 }
7776                 
7777                 if(camel == 'opacity') {
7778                     this.setOpacity(value);
7779                 }else{
7780                     this.dom.style[camel] = value;
7781                 }
7782             }else{
7783                 for(var style in prop){
7784                     if(typeof prop[style] != "function"){
7785                        this.setStyle(style, prop[style]);
7786                     }
7787                 }
7788             }
7789             return this;
7790         },
7791
7792         /**
7793          * More flexible version of {@link #setStyle} for setting style properties.
7794          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7795          * a function which returns such a specification.
7796          * @return {Roo.Element} this
7797          */
7798         applyStyles : function(style){
7799             Roo.DomHelper.applyStyles(this.dom, style);
7800             return this;
7801         },
7802
7803         /**
7804           * 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).
7805           * @return {Number} The X position of the element
7806           */
7807         getX : function(){
7808             return D.getX(this.dom);
7809         },
7810
7811         /**
7812           * 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).
7813           * @return {Number} The Y position of the element
7814           */
7815         getY : function(){
7816             return D.getY(this.dom);
7817         },
7818
7819         /**
7820           * 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).
7821           * @return {Array} The XY position of the element
7822           */
7823         getXY : function(){
7824             return D.getXY(this.dom);
7825         },
7826
7827         /**
7828          * 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).
7829          * @param {Number} The X position of the element
7830          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7831          * @return {Roo.Element} this
7832          */
7833         setX : function(x, animate){
7834             if(!animate || !A){
7835                 D.setX(this.dom, x);
7836             }else{
7837                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7838             }
7839             return this;
7840         },
7841
7842         /**
7843          * 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).
7844          * @param {Number} The Y position of the element
7845          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7846          * @return {Roo.Element} this
7847          */
7848         setY : function(y, animate){
7849             if(!animate || !A){
7850                 D.setY(this.dom, y);
7851             }else{
7852                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7853             }
7854             return this;
7855         },
7856
7857         /**
7858          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7859          * @param {String} left The left CSS property value
7860          * @return {Roo.Element} this
7861          */
7862         setLeft : function(left){
7863             this.setStyle("left", this.addUnits(left));
7864             return this;
7865         },
7866
7867         /**
7868          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7869          * @param {String} top The top CSS property value
7870          * @return {Roo.Element} this
7871          */
7872         setTop : function(top){
7873             this.setStyle("top", this.addUnits(top));
7874             return this;
7875         },
7876
7877         /**
7878          * Sets the element's CSS right style.
7879          * @param {String} right The right CSS property value
7880          * @return {Roo.Element} this
7881          */
7882         setRight : function(right){
7883             this.setStyle("right", this.addUnits(right));
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's CSS bottom style.
7889          * @param {String} bottom The bottom CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setBottom : function(bottom){
7893             this.setStyle("bottom", this.addUnits(bottom));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7899          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7900          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7901          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7902          * @return {Roo.Element} this
7903          */
7904         setXY : function(pos, animate){
7905             if(!animate || !A){
7906                 D.setXY(this.dom, pos);
7907             }else{
7908                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7909             }
7910             return this;
7911         },
7912
7913         /**
7914          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7915          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7916          * @param {Number} x X value for new position (coordinates are page-based)
7917          * @param {Number} y Y value for new position (coordinates are page-based)
7918          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7919          * @return {Roo.Element} this
7920          */
7921         setLocation : function(x, y, animate){
7922             this.setXY([x, y], this.preanim(arguments, 2));
7923             return this;
7924         },
7925
7926         /**
7927          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7928          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7929          * @param {Number} x X value for new position (coordinates are page-based)
7930          * @param {Number} y Y value for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         moveTo : function(x, y, animate){
7935             this.setXY([x, y], this.preanim(arguments, 2));
7936             return this;
7937         },
7938
7939         /**
7940          * Returns the region of the given element.
7941          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7942          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7943          */
7944         getRegion : function(){
7945             return D.getRegion(this.dom);
7946         },
7947
7948         /**
7949          * Returns the offset height of the element
7950          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7951          * @return {Number} The element's height
7952          */
7953         getHeight : function(contentHeight){
7954             var h = this.dom.offsetHeight || 0;
7955             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7956         },
7957
7958         /**
7959          * Returns the offset width of the element
7960          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7961          * @return {Number} The element's width
7962          */
7963         getWidth : function(contentWidth){
7964             var w = this.dom.offsetWidth || 0;
7965             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7966         },
7967
7968         /**
7969          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7970          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7971          * if a height has not been set using CSS.
7972          * @return {Number}
7973          */
7974         getComputedHeight : function(){
7975             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7976             if(!h){
7977                 h = parseInt(this.getStyle('height'), 10) || 0;
7978                 if(!this.isBorderBox()){
7979                     h += this.getFrameWidth('tb');
7980                 }
7981             }
7982             return h;
7983         },
7984
7985         /**
7986          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7987          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7988          * if a width has not been set using CSS.
7989          * @return {Number}
7990          */
7991         getComputedWidth : function(){
7992             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7993             if(!w){
7994                 w = parseInt(this.getStyle('width'), 10) || 0;
7995                 if(!this.isBorderBox()){
7996                     w += this.getFrameWidth('lr');
7997                 }
7998             }
7999             return w;
8000         },
8001
8002         /**
8003          * Returns the size of the element.
8004          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8005          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8006          */
8007         getSize : function(contentSize){
8008             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8009         },
8010
8011         /**
8012          * Returns the width and height of the viewport.
8013          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8014          */
8015         getViewSize : function(){
8016             var d = this.dom, doc = document, aw = 0, ah = 0;
8017             if(d == doc || d == doc.body){
8018                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8019             }else{
8020                 return {
8021                     width : d.clientWidth,
8022                     height: d.clientHeight
8023                 };
8024             }
8025         },
8026
8027         /**
8028          * Returns the value of the "value" attribute
8029          * @param {Boolean} asNumber true to parse the value as a number
8030          * @return {String/Number}
8031          */
8032         getValue : function(asNumber){
8033             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8034         },
8035
8036         // private
8037         adjustWidth : function(width){
8038             if(typeof width == "number"){
8039                 if(this.autoBoxAdjust && !this.isBorderBox()){
8040                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8041                 }
8042                 if(width < 0){
8043                     width = 0;
8044                 }
8045             }
8046             return width;
8047         },
8048
8049         // private
8050         adjustHeight : function(height){
8051             if(typeof height == "number"){
8052                if(this.autoBoxAdjust && !this.isBorderBox()){
8053                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8054                }
8055                if(height < 0){
8056                    height = 0;
8057                }
8058             }
8059             return height;
8060         },
8061
8062         /**
8063          * Set the width of the element
8064          * @param {Number} width The new width
8065          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8066          * @return {Roo.Element} this
8067          */
8068         setWidth : function(width, animate){
8069             width = this.adjustWidth(width);
8070             if(!animate || !A){
8071                 this.dom.style.width = this.addUnits(width);
8072             }else{
8073                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8074             }
8075             return this;
8076         },
8077
8078         /**
8079          * Set the height of the element
8080          * @param {Number} height The new height
8081          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8082          * @return {Roo.Element} this
8083          */
8084          setHeight : function(height, animate){
8085             height = this.adjustHeight(height);
8086             if(!animate || !A){
8087                 this.dom.style.height = this.addUnits(height);
8088             }else{
8089                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8090             }
8091             return this;
8092         },
8093
8094         /**
8095          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8096          * @param {Number} width The new width
8097          * @param {Number} height The new height
8098          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8099          * @return {Roo.Element} this
8100          */
8101          setSize : function(width, height, animate){
8102             if(typeof width == "object"){ // in case of object from getSize()
8103                 height = width.height; width = width.width;
8104             }
8105             width = this.adjustWidth(width); height = this.adjustHeight(height);
8106             if(!animate || !A){
8107                 this.dom.style.width = this.addUnits(width);
8108                 this.dom.style.height = this.addUnits(height);
8109             }else{
8110                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8111             }
8112             return this;
8113         },
8114
8115         /**
8116          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8117          * @param {Number} x X value for new position (coordinates are page-based)
8118          * @param {Number} y Y value for new position (coordinates are page-based)
8119          * @param {Number} width The new width
8120          * @param {Number} height The new height
8121          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8122          * @return {Roo.Element} this
8123          */
8124         setBounds : function(x, y, width, height, animate){
8125             if(!animate || !A){
8126                 this.setSize(width, height);
8127                 this.setLocation(x, y);
8128             }else{
8129                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8130                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8131                               this.preanim(arguments, 4), 'motion');
8132             }
8133             return this;
8134         },
8135
8136         /**
8137          * 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.
8138          * @param {Roo.lib.Region} region The region to fill
8139          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8140          * @return {Roo.Element} this
8141          */
8142         setRegion : function(region, animate){
8143             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8144             return this;
8145         },
8146
8147         /**
8148          * Appends an event handler
8149          *
8150          * @param {String}   eventName     The type of event to append
8151          * @param {Function} fn        The method the event invokes
8152          * @param {Object} scope       (optional) The scope (this object) of the fn
8153          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8154          */
8155         addListener : function(eventName, fn, scope, options){
8156             if (this.dom) {
8157                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8158             }
8159         },
8160
8161         /**
8162          * Removes an event handler from this element
8163          * @param {String} eventName the type of event to remove
8164          * @param {Function} fn the method the event invokes
8165          * @return {Roo.Element} this
8166          */
8167         removeListener : function(eventName, fn){
8168             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8169             return this;
8170         },
8171
8172         /**
8173          * Removes all previous added listeners from this element
8174          * @return {Roo.Element} this
8175          */
8176         removeAllListeners : function(){
8177             E.purgeElement(this.dom);
8178             return this;
8179         },
8180
8181         relayEvent : function(eventName, observable){
8182             this.on(eventName, function(e){
8183                 observable.fireEvent(eventName, e);
8184             });
8185         },
8186
8187         /**
8188          * Set the opacity of the element
8189          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8190          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8191          * @return {Roo.Element} this
8192          */
8193          setOpacity : function(opacity, animate){
8194             if(!animate || !A){
8195                 var s = this.dom.style;
8196                 if(Roo.isIE){
8197                     s.zoom = 1;
8198                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8199                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8200                 }else{
8201                     s.opacity = opacity;
8202                 }
8203             }else{
8204                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8205             }
8206             return this;
8207         },
8208
8209         /**
8210          * Gets the left X coordinate
8211          * @param {Boolean} local True to get the local css position instead of page coordinate
8212          * @return {Number}
8213          */
8214         getLeft : function(local){
8215             if(!local){
8216                 return this.getX();
8217             }else{
8218                 return parseInt(this.getStyle("left"), 10) || 0;
8219             }
8220         },
8221
8222         /**
8223          * Gets the right X coordinate of the element (element X position + element width)
8224          * @param {Boolean} local True to get the local css position instead of page coordinate
8225          * @return {Number}
8226          */
8227         getRight : function(local){
8228             if(!local){
8229                 return this.getX() + this.getWidth();
8230             }else{
8231                 return (this.getLeft(true) + this.getWidth()) || 0;
8232             }
8233         },
8234
8235         /**
8236          * Gets the top Y coordinate
8237          * @param {Boolean} local True to get the local css position instead of page coordinate
8238          * @return {Number}
8239          */
8240         getTop : function(local) {
8241             if(!local){
8242                 return this.getY();
8243             }else{
8244                 return parseInt(this.getStyle("top"), 10) || 0;
8245             }
8246         },
8247
8248         /**
8249          * Gets the bottom Y coordinate of the element (element Y position + element height)
8250          * @param {Boolean} local True to get the local css position instead of page coordinate
8251          * @return {Number}
8252          */
8253         getBottom : function(local){
8254             if(!local){
8255                 return this.getY() + this.getHeight();
8256             }else{
8257                 return (this.getTop(true) + this.getHeight()) || 0;
8258             }
8259         },
8260
8261         /**
8262         * Initializes positioning on this element. If a desired position is not passed, it will make the
8263         * the element positioned relative IF it is not already positioned.
8264         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8265         * @param {Number} zIndex (optional) The zIndex to apply
8266         * @param {Number} x (optional) Set the page X position
8267         * @param {Number} y (optional) Set the page Y position
8268         */
8269         position : function(pos, zIndex, x, y){
8270             if(!pos){
8271                if(this.getStyle('position') == 'static'){
8272                    this.setStyle('position', 'relative');
8273                }
8274             }else{
8275                 this.setStyle("position", pos);
8276             }
8277             if(zIndex){
8278                 this.setStyle("z-index", zIndex);
8279             }
8280             if(x !== undefined && y !== undefined){
8281                 this.setXY([x, y]);
8282             }else if(x !== undefined){
8283                 this.setX(x);
8284             }else if(y !== undefined){
8285                 this.setY(y);
8286             }
8287         },
8288
8289         /**
8290         * Clear positioning back to the default when the document was loaded
8291         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8292         * @return {Roo.Element} this
8293          */
8294         clearPositioning : function(value){
8295             value = value ||'';
8296             this.setStyle({
8297                 "left": value,
8298                 "right": value,
8299                 "top": value,
8300                 "bottom": value,
8301                 "z-index": "",
8302                 "position" : "static"
8303             });
8304             return this;
8305         },
8306
8307         /**
8308         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8309         * snapshot before performing an update and then restoring the element.
8310         * @return {Object}
8311         */
8312         getPositioning : function(){
8313             var l = this.getStyle("left");
8314             var t = this.getStyle("top");
8315             return {
8316                 "position" : this.getStyle("position"),
8317                 "left" : l,
8318                 "right" : l ? "" : this.getStyle("right"),
8319                 "top" : t,
8320                 "bottom" : t ? "" : this.getStyle("bottom"),
8321                 "z-index" : this.getStyle("z-index")
8322             };
8323         },
8324
8325         /**
8326          * Gets the width of the border(s) for the specified side(s)
8327          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8328          * passing lr would get the border (l)eft width + the border (r)ight width.
8329          * @return {Number} The width of the sides passed added together
8330          */
8331         getBorderWidth : function(side){
8332             return this.addStyles(side, El.borders);
8333         },
8334
8335         /**
8336          * Gets the width of the padding(s) for the specified side(s)
8337          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8338          * passing lr would get the padding (l)eft + the padding (r)ight.
8339          * @return {Number} The padding of the sides passed added together
8340          */
8341         getPadding : function(side){
8342             return this.addStyles(side, El.paddings);
8343         },
8344
8345         /**
8346         * Set positioning with an object returned by getPositioning().
8347         * @param {Object} posCfg
8348         * @return {Roo.Element} this
8349          */
8350         setPositioning : function(pc){
8351             this.applyStyles(pc);
8352             if(pc.right == "auto"){
8353                 this.dom.style.right = "";
8354             }
8355             if(pc.bottom == "auto"){
8356                 this.dom.style.bottom = "";
8357             }
8358             return this;
8359         },
8360
8361         // private
8362         fixDisplay : function(){
8363             if(this.getStyle("display") == "none"){
8364                 this.setStyle("visibility", "hidden");
8365                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8366                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8367                     this.setStyle("display", "block");
8368                 }
8369             }
8370         },
8371
8372         /**
8373          * Quick set left and top adding default units
8374          * @param {String} left The left CSS property value
8375          * @param {String} top The top CSS property value
8376          * @return {Roo.Element} this
8377          */
8378          setLeftTop : function(left, top){
8379             this.dom.style.left = this.addUnits(left);
8380             this.dom.style.top = this.addUnits(top);
8381             return this;
8382         },
8383
8384         /**
8385          * Move this element relative to its current position.
8386          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8387          * @param {Number} distance How far to move the element in pixels
8388          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8389          * @return {Roo.Element} this
8390          */
8391          move : function(direction, distance, animate){
8392             var xy = this.getXY();
8393             direction = direction.toLowerCase();
8394             switch(direction){
8395                 case "l":
8396                 case "left":
8397                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8398                     break;
8399                case "r":
8400                case "right":
8401                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8402                     break;
8403                case "t":
8404                case "top":
8405                case "up":
8406                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8407                     break;
8408                case "b":
8409                case "bottom":
8410                case "down":
8411                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8412                     break;
8413             }
8414             return this;
8415         },
8416
8417         /**
8418          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8419          * @return {Roo.Element} this
8420          */
8421         clip : function(){
8422             if(!this.isClipped){
8423                this.isClipped = true;
8424                this.originalClip = {
8425                    "o": this.getStyle("overflow"),
8426                    "x": this.getStyle("overflow-x"),
8427                    "y": this.getStyle("overflow-y")
8428                };
8429                this.setStyle("overflow", "hidden");
8430                this.setStyle("overflow-x", "hidden");
8431                this.setStyle("overflow-y", "hidden");
8432             }
8433             return this;
8434         },
8435
8436         /**
8437          *  Return clipping (overflow) to original clipping before clip() was called
8438          * @return {Roo.Element} this
8439          */
8440         unclip : function(){
8441             if(this.isClipped){
8442                 this.isClipped = false;
8443                 var o = this.originalClip;
8444                 if(o.o){this.setStyle("overflow", o.o);}
8445                 if(o.x){this.setStyle("overflow-x", o.x);}
8446                 if(o.y){this.setStyle("overflow-y", o.y);}
8447             }
8448             return this;
8449         },
8450
8451
8452         /**
8453          * Gets the x,y coordinates specified by the anchor position on the element.
8454          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8455          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8456          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8457          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8458          * @return {Array} [x, y] An array containing the element's x and y coordinates
8459          */
8460         getAnchorXY : function(anchor, local, s){
8461             //Passing a different size is useful for pre-calculating anchors,
8462             //especially for anchored animations that change the el size.
8463
8464             var w, h, vp = false;
8465             if(!s){
8466                 var d = this.dom;
8467                 if(d == document.body || d == document){
8468                     vp = true;
8469                     w = D.getViewWidth(); h = D.getViewHeight();
8470                 }else{
8471                     w = this.getWidth(); h = this.getHeight();
8472                 }
8473             }else{
8474                 w = s.width;  h = s.height;
8475             }
8476             var x = 0, y = 0, r = Math.round;
8477             switch((anchor || "tl").toLowerCase()){
8478                 case "c":
8479                     x = r(w*.5);
8480                     y = r(h*.5);
8481                 break;
8482                 case "t":
8483                     x = r(w*.5);
8484                     y = 0;
8485                 break;
8486                 case "l":
8487                     x = 0;
8488                     y = r(h*.5);
8489                 break;
8490                 case "r":
8491                     x = w;
8492                     y = r(h*.5);
8493                 break;
8494                 case "b":
8495                     x = r(w*.5);
8496                     y = h;
8497                 break;
8498                 case "tl":
8499                     x = 0;
8500                     y = 0;
8501                 break;
8502                 case "bl":
8503                     x = 0;
8504                     y = h;
8505                 break;
8506                 case "br":
8507                     x = w;
8508                     y = h;
8509                 break;
8510                 case "tr":
8511                     x = w;
8512                     y = 0;
8513                 break;
8514             }
8515             if(local === true){
8516                 return [x, y];
8517             }
8518             if(vp){
8519                 var sc = this.getScroll();
8520                 return [x + sc.left, y + sc.top];
8521             }
8522             //Add the element's offset xy
8523             var o = this.getXY();
8524             return [x+o[0], y+o[1]];
8525         },
8526
8527         /**
8528          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8529          * supported position values.
8530          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8531          * @param {String} position The position to align to.
8532          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8533          * @return {Array} [x, y]
8534          */
8535         getAlignToXY : function(el, p, o){
8536             el = Roo.get(el);
8537             var d = this.dom;
8538             if(!el.dom){
8539                 throw "Element.alignTo with an element that doesn't exist";
8540             }
8541             var c = false; //constrain to viewport
8542             var p1 = "", p2 = "";
8543             o = o || [0,0];
8544
8545             if(!p){
8546                 p = "tl-bl";
8547             }else if(p == "?"){
8548                 p = "tl-bl?";
8549             }else if(p.indexOf("-") == -1){
8550                 p = "tl-" + p;
8551             }
8552             p = p.toLowerCase();
8553             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8554             if(!m){
8555                throw "Element.alignTo with an invalid alignment " + p;
8556             }
8557             p1 = m[1]; p2 = m[2]; c = !!m[3];
8558
8559             //Subtract the aligned el's internal xy from the target's offset xy
8560             //plus custom offset to get the aligned el's new offset xy
8561             var a1 = this.getAnchorXY(p1, true);
8562             var a2 = el.getAnchorXY(p2, false);
8563             var x = a2[0] - a1[0] + o[0];
8564             var y = a2[1] - a1[1] + o[1];
8565             if(c){
8566                 //constrain the aligned el to viewport if necessary
8567                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8568                 // 5px of margin for ie
8569                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8570
8571                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8572                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8573                 //otherwise swap the aligned el to the opposite border of the target.
8574                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8575                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8576                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8577                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8578
8579                var doc = document;
8580                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8581                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8582
8583                if((x+w) > dw + scrollX){
8584                     x = swapX ? r.left-w : dw+scrollX-w;
8585                 }
8586                if(x < scrollX){
8587                    x = swapX ? r.right : scrollX;
8588                }
8589                if((y+h) > dh + scrollY){
8590                     y = swapY ? r.top-h : dh+scrollY-h;
8591                 }
8592                if (y < scrollY){
8593                    y = swapY ? r.bottom : scrollY;
8594                }
8595             }
8596             return [x,y];
8597         },
8598
8599         // private
8600         getConstrainToXY : function(){
8601             var os = {top:0, left:0, bottom:0, right: 0};
8602
8603             return function(el, local, offsets, proposedXY){
8604                 el = Roo.get(el);
8605                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8606
8607                 var vw, vh, vx = 0, vy = 0;
8608                 if(el.dom == document.body || el.dom == document){
8609                     vw = Roo.lib.Dom.getViewWidth();
8610                     vh = Roo.lib.Dom.getViewHeight();
8611                 }else{
8612                     vw = el.dom.clientWidth;
8613                     vh = el.dom.clientHeight;
8614                     if(!local){
8615                         var vxy = el.getXY();
8616                         vx = vxy[0];
8617                         vy = vxy[1];
8618                     }
8619                 }
8620
8621                 var s = el.getScroll();
8622
8623                 vx += offsets.left + s.left;
8624                 vy += offsets.top + s.top;
8625
8626                 vw -= offsets.right;
8627                 vh -= offsets.bottom;
8628
8629                 var vr = vx+vw;
8630                 var vb = vy+vh;
8631
8632                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8633                 var x = xy[0], y = xy[1];
8634                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8635
8636                 // only move it if it needs it
8637                 var moved = false;
8638
8639                 // first validate right/bottom
8640                 if((x + w) > vr){
8641                     x = vr - w;
8642                     moved = true;
8643                 }
8644                 if((y + h) > vb){
8645                     y = vb - h;
8646                     moved = true;
8647                 }
8648                 // then make sure top/left isn't negative
8649                 if(x < vx){
8650                     x = vx;
8651                     moved = true;
8652                 }
8653                 if(y < vy){
8654                     y = vy;
8655                     moved = true;
8656                 }
8657                 return moved ? [x, y] : false;
8658             };
8659         }(),
8660
8661         // private
8662         adjustForConstraints : function(xy, parent, offsets){
8663             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8664         },
8665
8666         /**
8667          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8668          * document it aligns it to the viewport.
8669          * The position parameter is optional, and can be specified in any one of the following formats:
8670          * <ul>
8671          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8672          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8673          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8674          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8675          *   <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
8676          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8677          * </ul>
8678          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8679          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8680          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8681          * that specified in order to enforce the viewport constraints.
8682          * Following are all of the supported anchor positions:
8683     <pre>
8684     Value  Description
8685     -----  -----------------------------
8686     tl     The top left corner (default)
8687     t      The center of the top edge
8688     tr     The top right corner
8689     l      The center of the left edge
8690     c      In the center of the element
8691     r      The center of the right edge
8692     bl     The bottom left corner
8693     b      The center of the bottom edge
8694     br     The bottom right corner
8695     </pre>
8696     Example Usage:
8697     <pre><code>
8698     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8699     el.alignTo("other-el");
8700
8701     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8702     el.alignTo("other-el", "tr?");
8703
8704     // align the bottom right corner of el with the center left edge of other-el
8705     el.alignTo("other-el", "br-l?");
8706
8707     // align the center of el with the bottom left corner of other-el and
8708     // adjust the x position by -6 pixels (and the y position by 0)
8709     el.alignTo("other-el", "c-bl", [-6, 0]);
8710     </code></pre>
8711          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8712          * @param {String} position The position to align to.
8713          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8714          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8715          * @return {Roo.Element} this
8716          */
8717         alignTo : function(element, position, offsets, animate){
8718             var xy = this.getAlignToXY(element, position, offsets);
8719             this.setXY(xy, this.preanim(arguments, 3));
8720             return this;
8721         },
8722
8723         /**
8724          * Anchors an element to another element and realigns it when the window is resized.
8725          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8726          * @param {String} position The position to align to.
8727          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8728          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8729          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8730          * is a number, it is used as the buffer delay (defaults to 50ms).
8731          * @param {Function} callback The function to call after the animation finishes
8732          * @return {Roo.Element} this
8733          */
8734         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8735             var action = function(){
8736                 this.alignTo(el, alignment, offsets, animate);
8737                 Roo.callback(callback, this);
8738             };
8739             Roo.EventManager.onWindowResize(action, this);
8740             var tm = typeof monitorScroll;
8741             if(tm != 'undefined'){
8742                 Roo.EventManager.on(window, 'scroll', action, this,
8743                     {buffer: tm == 'number' ? monitorScroll : 50});
8744             }
8745             action.call(this); // align immediately
8746             return this;
8747         },
8748         /**
8749          * Clears any opacity settings from this element. Required in some cases for IE.
8750          * @return {Roo.Element} this
8751          */
8752         clearOpacity : function(){
8753             if (window.ActiveXObject) {
8754                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8755                     this.dom.style.filter = "";
8756                 }
8757             } else {
8758                 this.dom.style.opacity = "";
8759                 this.dom.style["-moz-opacity"] = "";
8760                 this.dom.style["-khtml-opacity"] = "";
8761             }
8762             return this;
8763         },
8764
8765         /**
8766          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8767          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8768          * @return {Roo.Element} this
8769          */
8770         hide : function(animate){
8771             this.setVisible(false, this.preanim(arguments, 0));
8772             return this;
8773         },
8774
8775         /**
8776         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8777         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8778          * @return {Roo.Element} this
8779          */
8780         show : function(animate){
8781             this.setVisible(true, this.preanim(arguments, 0));
8782             return this;
8783         },
8784
8785         /**
8786          * @private Test if size has a unit, otherwise appends the default
8787          */
8788         addUnits : function(size){
8789             return Roo.Element.addUnits(size, this.defaultUnit);
8790         },
8791
8792         /**
8793          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8794          * @return {Roo.Element} this
8795          */
8796         beginMeasure : function(){
8797             var el = this.dom;
8798             if(el.offsetWidth || el.offsetHeight){
8799                 return this; // offsets work already
8800             }
8801             var changed = [];
8802             var p = this.dom, b = document.body; // start with this element
8803             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8804                 var pe = Roo.get(p);
8805                 if(pe.getStyle('display') == 'none'){
8806                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8807                     p.style.visibility = "hidden";
8808                     p.style.display = "block";
8809                 }
8810                 p = p.parentNode;
8811             }
8812             this._measureChanged = changed;
8813             return this;
8814
8815         },
8816
8817         /**
8818          * Restores displays to before beginMeasure was called
8819          * @return {Roo.Element} this
8820          */
8821         endMeasure : function(){
8822             var changed = this._measureChanged;
8823             if(changed){
8824                 for(var i = 0, len = changed.length; i < len; i++) {
8825                     var r = changed[i];
8826                     r.el.style.visibility = r.visibility;
8827                     r.el.style.display = "none";
8828                 }
8829                 this._measureChanged = null;
8830             }
8831             return this;
8832         },
8833
8834         /**
8835         * Update the innerHTML of this element, optionally searching for and processing scripts
8836         * @param {String} html The new HTML
8837         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8838         * @param {Function} callback For async script loading you can be noticed when the update completes
8839         * @return {Roo.Element} this
8840          */
8841         update : function(html, loadScripts, callback){
8842             if(typeof html == "undefined"){
8843                 html = "";
8844             }
8845             if(loadScripts !== true){
8846                 this.dom.innerHTML = html;
8847                 if(typeof callback == "function"){
8848                     callback();
8849                 }
8850                 return this;
8851             }
8852             var id = Roo.id();
8853             var dom = this.dom;
8854
8855             html += '<span id="' + id + '"></span>';
8856
8857             E.onAvailable(id, function(){
8858                 var hd = document.getElementsByTagName("head")[0];
8859                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8860                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8861                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8862
8863                 var match;
8864                 while(match = re.exec(html)){
8865                     var attrs = match[1];
8866                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8867                     if(srcMatch && srcMatch[2]){
8868                        var s = document.createElement("script");
8869                        s.src = srcMatch[2];
8870                        var typeMatch = attrs.match(typeRe);
8871                        if(typeMatch && typeMatch[2]){
8872                            s.type = typeMatch[2];
8873                        }
8874                        hd.appendChild(s);
8875                     }else if(match[2] && match[2].length > 0){
8876                         if(window.execScript) {
8877                            window.execScript(match[2]);
8878                         } else {
8879                             /**
8880                              * eval:var:id
8881                              * eval:var:dom
8882                              * eval:var:html
8883                              * 
8884                              */
8885                            window.eval(match[2]);
8886                         }
8887                     }
8888                 }
8889                 var el = document.getElementById(id);
8890                 if(el){el.parentNode.removeChild(el);}
8891                 if(typeof callback == "function"){
8892                     callback();
8893                 }
8894             });
8895             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8896             return this;
8897         },
8898
8899         /**
8900          * Direct access to the UpdateManager update() method (takes the same parameters).
8901          * @param {String/Function} url The url for this request or a function to call to get the url
8902          * @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}
8903          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8904          * @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.
8905          * @return {Roo.Element} this
8906          */
8907         load : function(){
8908             var um = this.getUpdateManager();
8909             um.update.apply(um, arguments);
8910             return this;
8911         },
8912
8913         /**
8914         * Gets this element's UpdateManager
8915         * @return {Roo.UpdateManager} The UpdateManager
8916         */
8917         getUpdateManager : function(){
8918             if(!this.updateManager){
8919                 this.updateManager = new Roo.UpdateManager(this);
8920             }
8921             return this.updateManager;
8922         },
8923
8924         /**
8925          * Disables text selection for this element (normalized across browsers)
8926          * @return {Roo.Element} this
8927          */
8928         unselectable : function(){
8929             this.dom.unselectable = "on";
8930             this.swallowEvent("selectstart", true);
8931             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8932             this.addClass("x-unselectable");
8933             return this;
8934         },
8935
8936         /**
8937         * Calculates the x, y to center this element on the screen
8938         * @return {Array} The x, y values [x, y]
8939         */
8940         getCenterXY : function(){
8941             return this.getAlignToXY(document, 'c-c');
8942         },
8943
8944         /**
8945         * Centers the Element in either the viewport, or another Element.
8946         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8947         */
8948         center : function(centerIn){
8949             this.alignTo(centerIn || document, 'c-c');
8950             return this;
8951         },
8952
8953         /**
8954          * Tests various css rules/browsers to determine if this element uses a border box
8955          * @return {Boolean}
8956          */
8957         isBorderBox : function(){
8958             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8959         },
8960
8961         /**
8962          * Return a box {x, y, width, height} that can be used to set another elements
8963          * size/location to match this element.
8964          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8965          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8966          * @return {Object} box An object in the format {x, y, width, height}
8967          */
8968         getBox : function(contentBox, local){
8969             var xy;
8970             if(!local){
8971                 xy = this.getXY();
8972             }else{
8973                 var left = parseInt(this.getStyle("left"), 10) || 0;
8974                 var top = parseInt(this.getStyle("top"), 10) || 0;
8975                 xy = [left, top];
8976             }
8977             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8978             if(!contentBox){
8979                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8980             }else{
8981                 var l = this.getBorderWidth("l")+this.getPadding("l");
8982                 var r = this.getBorderWidth("r")+this.getPadding("r");
8983                 var t = this.getBorderWidth("t")+this.getPadding("t");
8984                 var b = this.getBorderWidth("b")+this.getPadding("b");
8985                 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)};
8986             }
8987             bx.right = bx.x + bx.width;
8988             bx.bottom = bx.y + bx.height;
8989             return bx;
8990         },
8991
8992         /**
8993          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8994          for more information about the sides.
8995          * @param {String} sides
8996          * @return {Number}
8997          */
8998         getFrameWidth : function(sides, onlyContentBox){
8999             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9000         },
9001
9002         /**
9003          * 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.
9004          * @param {Object} box The box to fill {x, y, width, height}
9005          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9006          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9007          * @return {Roo.Element} this
9008          */
9009         setBox : function(box, adjust, animate){
9010             var w = box.width, h = box.height;
9011             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9012                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9013                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9014             }
9015             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9016             return this;
9017         },
9018
9019         /**
9020          * Forces the browser to repaint this element
9021          * @return {Roo.Element} this
9022          */
9023          repaint : function(){
9024             var dom = this.dom;
9025             this.addClass("x-repaint");
9026             setTimeout(function(){
9027                 Roo.get(dom).removeClass("x-repaint");
9028             }, 1);
9029             return this;
9030         },
9031
9032         /**
9033          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9034          * then it returns the calculated width of the sides (see getPadding)
9035          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9036          * @return {Object/Number}
9037          */
9038         getMargins : function(side){
9039             if(!side){
9040                 return {
9041                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9042                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9043                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9044                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9045                 };
9046             }else{
9047                 return this.addStyles(side, El.margins);
9048              }
9049         },
9050
9051         // private
9052         addStyles : function(sides, styles){
9053             var val = 0, v, w;
9054             for(var i = 0, len = sides.length; i < len; i++){
9055                 v = this.getStyle(styles[sides.charAt(i)]);
9056                 if(v){
9057                      w = parseInt(v, 10);
9058                      if(w){ val += w; }
9059                 }
9060             }
9061             return val;
9062         },
9063
9064         /**
9065          * Creates a proxy element of this element
9066          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9067          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9068          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9069          * @return {Roo.Element} The new proxy element
9070          */
9071         createProxy : function(config, renderTo, matchBox){
9072             if(renderTo){
9073                 renderTo = Roo.getDom(renderTo);
9074             }else{
9075                 renderTo = document.body;
9076             }
9077             config = typeof config == "object" ?
9078                 config : {tag : "div", cls: config};
9079             var proxy = Roo.DomHelper.append(renderTo, config, true);
9080             if(matchBox){
9081                proxy.setBox(this.getBox());
9082             }
9083             return proxy;
9084         },
9085
9086         /**
9087          * Puts a mask over this element to disable user interaction. Requires core.css.
9088          * This method can only be applied to elements which accept child nodes.
9089          * @param {String} msg (optional) A message to display in the mask
9090          * @param {String} msgCls (optional) A css class to apply to the msg element
9091          * @return {Element} The mask  element
9092          */
9093         mask : function(msg, msgCls)
9094         {
9095             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9096                 this.setStyle("position", "relative");
9097             }
9098             if(!this._mask){
9099                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9100             }
9101             this.addClass("x-masked");
9102             this._mask.setDisplayed(true);
9103             
9104             // we wander
9105             var z = 0;
9106             var dom = this.dom;
9107             while (dom && dom.style) {
9108                 if (!isNaN(parseInt(dom.style.zIndex))) {
9109                     z = Math.max(z, parseInt(dom.style.zIndex));
9110                 }
9111                 dom = dom.parentNode;
9112             }
9113             // if we are masking the body - then it hides everything..
9114             if (this.dom == document.body) {
9115                 z = 1000000;
9116                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9117                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9118             }
9119            
9120             if(typeof msg == 'string'){
9121                 if(!this._maskMsg){
9122                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9123                 }
9124                 var mm = this._maskMsg;
9125                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9126                 if (mm.dom.firstChild) { // weird IE issue?
9127                     mm.dom.firstChild.innerHTML = msg;
9128                 }
9129                 mm.setDisplayed(true);
9130                 mm.center(this);
9131                 mm.setStyle('z-index', z + 102);
9132             }
9133             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9134                 this._mask.setHeight(this.getHeight());
9135             }
9136             this._mask.setStyle('z-index', z + 100);
9137             
9138             return this._mask;
9139         },
9140
9141         /**
9142          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9143          * it is cached for reuse.
9144          */
9145         unmask : function(removeEl){
9146             if(this._mask){
9147                 if(removeEl === true){
9148                     this._mask.remove();
9149                     delete this._mask;
9150                     if(this._maskMsg){
9151                         this._maskMsg.remove();
9152                         delete this._maskMsg;
9153                     }
9154                 }else{
9155                     this._mask.setDisplayed(false);
9156                     if(this._maskMsg){
9157                         this._maskMsg.setDisplayed(false);
9158                     }
9159                 }
9160             }
9161             this.removeClass("x-masked");
9162         },
9163
9164         /**
9165          * Returns true if this element is masked
9166          * @return {Boolean}
9167          */
9168         isMasked : function(){
9169             return this._mask && this._mask.isVisible();
9170         },
9171
9172         /**
9173          * Creates an iframe shim for this element to keep selects and other windowed objects from
9174          * showing through.
9175          * @return {Roo.Element} The new shim element
9176          */
9177         createShim : function(){
9178             var el = document.createElement('iframe');
9179             el.frameBorder = 'no';
9180             el.className = 'roo-shim';
9181             if(Roo.isIE && Roo.isSecure){
9182                 el.src = Roo.SSL_SECURE_URL;
9183             }
9184             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9185             shim.autoBoxAdjust = false;
9186             return shim;
9187         },
9188
9189         /**
9190          * Removes this element from the DOM and deletes it from the cache
9191          */
9192         remove : function(){
9193             if(this.dom.parentNode){
9194                 this.dom.parentNode.removeChild(this.dom);
9195             }
9196             delete El.cache[this.dom.id];
9197         },
9198
9199         /**
9200          * Sets up event handlers to add and remove a css class when the mouse is over this element
9201          * @param {String} className
9202          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9203          * mouseout events for children elements
9204          * @return {Roo.Element} this
9205          */
9206         addClassOnOver : function(className, preventFlicker){
9207             this.on("mouseover", function(){
9208                 Roo.fly(this, '_internal').addClass(className);
9209             }, this.dom);
9210             var removeFn = function(e){
9211                 if(preventFlicker !== true || !e.within(this, true)){
9212                     Roo.fly(this, '_internal').removeClass(className);
9213                 }
9214             };
9215             this.on("mouseout", removeFn, this.dom);
9216             return this;
9217         },
9218
9219         /**
9220          * Sets up event handlers to add and remove a css class when this element has the focus
9221          * @param {String} className
9222          * @return {Roo.Element} this
9223          */
9224         addClassOnFocus : function(className){
9225             this.on("focus", function(){
9226                 Roo.fly(this, '_internal').addClass(className);
9227             }, this.dom);
9228             this.on("blur", function(){
9229                 Roo.fly(this, '_internal').removeClass(className);
9230             }, this.dom);
9231             return this;
9232         },
9233         /**
9234          * 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)
9235          * @param {String} className
9236          * @return {Roo.Element} this
9237          */
9238         addClassOnClick : function(className){
9239             var dom = this.dom;
9240             this.on("mousedown", function(){
9241                 Roo.fly(dom, '_internal').addClass(className);
9242                 var d = Roo.get(document);
9243                 var fn = function(){
9244                     Roo.fly(dom, '_internal').removeClass(className);
9245                     d.removeListener("mouseup", fn);
9246                 };
9247                 d.on("mouseup", fn);
9248             });
9249             return this;
9250         },
9251
9252         /**
9253          * Stops the specified event from bubbling and optionally prevents the default action
9254          * @param {String} eventName
9255          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9256          * @return {Roo.Element} this
9257          */
9258         swallowEvent : function(eventName, preventDefault){
9259             var fn = function(e){
9260                 e.stopPropagation();
9261                 if(preventDefault){
9262                     e.preventDefault();
9263                 }
9264             };
9265             if(eventName instanceof Array){
9266                 for(var i = 0, len = eventName.length; i < len; i++){
9267                      this.on(eventName[i], fn);
9268                 }
9269                 return this;
9270             }
9271             this.on(eventName, fn);
9272             return this;
9273         },
9274
9275         /**
9276          * @private
9277          */
9278       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9279
9280         /**
9281          * Sizes this element to its parent element's dimensions performing
9282          * neccessary box adjustments.
9283          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9284          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9285          * @return {Roo.Element} this
9286          */
9287         fitToParent : function(monitorResize, targetParent) {
9288           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9289           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9290           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9291             return;
9292           }
9293           var p = Roo.get(targetParent || this.dom.parentNode);
9294           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9295           if (monitorResize === true) {
9296             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9297             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9298           }
9299           return this;
9300         },
9301
9302         /**
9303          * Gets the next sibling, skipping text nodes
9304          * @return {HTMLElement} The next sibling or null
9305          */
9306         getNextSibling : function(){
9307             var n = this.dom.nextSibling;
9308             while(n && n.nodeType != 1){
9309                 n = n.nextSibling;
9310             }
9311             return n;
9312         },
9313
9314         /**
9315          * Gets the previous sibling, skipping text nodes
9316          * @return {HTMLElement} The previous sibling or null
9317          */
9318         getPrevSibling : function(){
9319             var n = this.dom.previousSibling;
9320             while(n && n.nodeType != 1){
9321                 n = n.previousSibling;
9322             }
9323             return n;
9324         },
9325
9326
9327         /**
9328          * Appends the passed element(s) to this element
9329          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9330          * @return {Roo.Element} this
9331          */
9332         appendChild: function(el){
9333             el = Roo.get(el);
9334             el.appendTo(this);
9335             return this;
9336         },
9337
9338         /**
9339          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9340          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9341          * automatically generated with the specified attributes.
9342          * @param {HTMLElement} insertBefore (optional) a child element of this element
9343          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9344          * @return {Roo.Element} The new child element
9345          */
9346         createChild: function(config, insertBefore, returnDom){
9347             config = config || {tag:'div'};
9348             if(insertBefore){
9349                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9350             }
9351             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9352         },
9353
9354         /**
9355          * Appends this element to the passed element
9356          * @param {String/HTMLElement/Element} el The new parent element
9357          * @return {Roo.Element} this
9358          */
9359         appendTo: function(el){
9360             el = Roo.getDom(el);
9361             el.appendChild(this.dom);
9362             return this;
9363         },
9364
9365         /**
9366          * Inserts this element before the passed element in the DOM
9367          * @param {String/HTMLElement/Element} el The element to insert before
9368          * @return {Roo.Element} this
9369          */
9370         insertBefore: function(el){
9371             el = Roo.getDom(el);
9372             el.parentNode.insertBefore(this.dom, el);
9373             return this;
9374         },
9375
9376         /**
9377          * Inserts this element after the passed element in the DOM
9378          * @param {String/HTMLElement/Element} el The element to insert after
9379          * @return {Roo.Element} this
9380          */
9381         insertAfter: function(el){
9382             el = Roo.getDom(el);
9383             el.parentNode.insertBefore(this.dom, el.nextSibling);
9384             return this;
9385         },
9386
9387         /**
9388          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9389          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9390          * @return {Roo.Element} The new child
9391          */
9392         insertFirst: function(el, returnDom){
9393             el = el || {};
9394             if(typeof el == 'object' && !el.nodeType){ // dh config
9395                 return this.createChild(el, this.dom.firstChild, returnDom);
9396             }else{
9397                 el = Roo.getDom(el);
9398                 this.dom.insertBefore(el, this.dom.firstChild);
9399                 return !returnDom ? Roo.get(el) : el;
9400             }
9401         },
9402
9403         /**
9404          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9405          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9406          * @param {String} where (optional) 'before' or 'after' defaults to before
9407          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9408          * @return {Roo.Element} the inserted Element
9409          */
9410         insertSibling: function(el, where, returnDom){
9411             where = where ? where.toLowerCase() : 'before';
9412             el = el || {};
9413             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9414
9415             if(typeof el == 'object' && !el.nodeType){ // dh config
9416                 if(where == 'after' && !this.dom.nextSibling){
9417                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9418                 }else{
9419                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9420                 }
9421
9422             }else{
9423                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9424                             where == 'before' ? this.dom : this.dom.nextSibling);
9425                 if(!returnDom){
9426                     rt = Roo.get(rt);
9427                 }
9428             }
9429             return rt;
9430         },
9431
9432         /**
9433          * Creates and wraps this element with another element
9434          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9435          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9436          * @return {HTMLElement/Element} The newly created wrapper element
9437          */
9438         wrap: function(config, returnDom){
9439             if(!config){
9440                 config = {tag: "div"};
9441             }
9442             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9443             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9444             return newEl;
9445         },
9446
9447         /**
9448          * Replaces the passed element with this element
9449          * @param {String/HTMLElement/Element} el The element to replace
9450          * @return {Roo.Element} this
9451          */
9452         replace: function(el){
9453             el = Roo.get(el);
9454             this.insertBefore(el);
9455             el.remove();
9456             return this;
9457         },
9458
9459         /**
9460          * Inserts an html fragment into this element
9461          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9462          * @param {String} html The HTML fragment
9463          * @param {Boolean} returnEl True to return an Roo.Element
9464          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9465          */
9466         insertHtml : function(where, html, returnEl){
9467             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9468             return returnEl ? Roo.get(el) : el;
9469         },
9470
9471         /**
9472          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9473          * @param {Object} o The object with the attributes
9474          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9475          * @return {Roo.Element} this
9476          */
9477         set : function(o, useSet){
9478             var el = this.dom;
9479             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9480             for(var attr in o){
9481                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9482                 if(attr=="cls"){
9483                     el.className = o["cls"];
9484                 }else{
9485                     if(useSet) {
9486                         el.setAttribute(attr, o[attr]);
9487                     } else {
9488                         el[attr] = o[attr];
9489                     }
9490                 }
9491             }
9492             if(o.style){
9493                 Roo.DomHelper.applyStyles(el, o.style);
9494             }
9495             return this;
9496         },
9497
9498         /**
9499          * Convenience method for constructing a KeyMap
9500          * @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:
9501          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9502          * @param {Function} fn The function to call
9503          * @param {Object} scope (optional) The scope of the function
9504          * @return {Roo.KeyMap} The KeyMap created
9505          */
9506         addKeyListener : function(key, fn, scope){
9507             var config;
9508             if(typeof key != "object" || key instanceof Array){
9509                 config = {
9510                     key: key,
9511                     fn: fn,
9512                     scope: scope
9513                 };
9514             }else{
9515                 config = {
9516                     key : key.key,
9517                     shift : key.shift,
9518                     ctrl : key.ctrl,
9519                     alt : key.alt,
9520                     fn: fn,
9521                     scope: scope
9522                 };
9523             }
9524             return new Roo.KeyMap(this, config);
9525         },
9526
9527         /**
9528          * Creates a KeyMap for this element
9529          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9530          * @return {Roo.KeyMap} The KeyMap created
9531          */
9532         addKeyMap : function(config){
9533             return new Roo.KeyMap(this, config);
9534         },
9535
9536         /**
9537          * Returns true if this element is scrollable.
9538          * @return {Boolean}
9539          */
9540          isScrollable : function(){
9541             var dom = this.dom;
9542             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9543         },
9544
9545         /**
9546          * 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().
9547          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9548          * @param {Number} value The new scroll value
9549          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9550          * @return {Element} this
9551          */
9552
9553         scrollTo : function(side, value, animate){
9554             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9555             if(!animate || !A){
9556                 this.dom[prop] = value;
9557             }else{
9558                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9559                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9560             }
9561             return this;
9562         },
9563
9564         /**
9565          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9566          * within this element's scrollable range.
9567          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9568          * @param {Number} distance How far to scroll the element in pixels
9569          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9570          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9571          * was scrolled as far as it could go.
9572          */
9573          scroll : function(direction, distance, animate){
9574              if(!this.isScrollable()){
9575                  return;
9576              }
9577              var el = this.dom;
9578              var l = el.scrollLeft, t = el.scrollTop;
9579              var w = el.scrollWidth, h = el.scrollHeight;
9580              var cw = el.clientWidth, ch = el.clientHeight;
9581              direction = direction.toLowerCase();
9582              var scrolled = false;
9583              var a = this.preanim(arguments, 2);
9584              switch(direction){
9585                  case "l":
9586                  case "left":
9587                      if(w - l > cw){
9588                          var v = Math.min(l + distance, w-cw);
9589                          this.scrollTo("left", v, a);
9590                          scrolled = true;
9591                      }
9592                      break;
9593                 case "r":
9594                 case "right":
9595                      if(l > 0){
9596                          var v = Math.max(l - distance, 0);
9597                          this.scrollTo("left", v, a);
9598                          scrolled = true;
9599                      }
9600                      break;
9601                 case "t":
9602                 case "top":
9603                 case "up":
9604                      if(t > 0){
9605                          var v = Math.max(t - distance, 0);
9606                          this.scrollTo("top", v, a);
9607                          scrolled = true;
9608                      }
9609                      break;
9610                 case "b":
9611                 case "bottom":
9612                 case "down":
9613                      if(h - t > ch){
9614                          var v = Math.min(t + distance, h-ch);
9615                          this.scrollTo("top", v, a);
9616                          scrolled = true;
9617                      }
9618                      break;
9619              }
9620              return scrolled;
9621         },
9622
9623         /**
9624          * Translates the passed page coordinates into left/top css values for this element
9625          * @param {Number/Array} x The page x or an array containing [x, y]
9626          * @param {Number} y The page y
9627          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9628          */
9629         translatePoints : function(x, y){
9630             if(typeof x == 'object' || x instanceof Array){
9631                 y = x[1]; x = x[0];
9632             }
9633             var p = this.getStyle('position');
9634             var o = this.getXY();
9635
9636             var l = parseInt(this.getStyle('left'), 10);
9637             var t = parseInt(this.getStyle('top'), 10);
9638
9639             if(isNaN(l)){
9640                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9641             }
9642             if(isNaN(t)){
9643                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9644             }
9645
9646             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9647         },
9648
9649         /**
9650          * Returns the current scroll position of the element.
9651          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9652          */
9653         getScroll : function(){
9654             var d = this.dom, doc = document;
9655             if(d == doc || d == doc.body){
9656                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9657                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9658                 return {left: l, top: t};
9659             }else{
9660                 return {left: d.scrollLeft, top: d.scrollTop};
9661             }
9662         },
9663
9664         /**
9665          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9666          * are convert to standard 6 digit hex color.
9667          * @param {String} attr The css attribute
9668          * @param {String} defaultValue The default value to use when a valid color isn't found
9669          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9670          * YUI color anims.
9671          */
9672         getColor : function(attr, defaultValue, prefix){
9673             var v = this.getStyle(attr);
9674             if(!v || v == "transparent" || v == "inherit") {
9675                 return defaultValue;
9676             }
9677             var color = typeof prefix == "undefined" ? "#" : prefix;
9678             if(v.substr(0, 4) == "rgb("){
9679                 var rvs = v.slice(4, v.length -1).split(",");
9680                 for(var i = 0; i < 3; i++){
9681                     var h = parseInt(rvs[i]).toString(16);
9682                     if(h < 16){
9683                         h = "0" + h;
9684                     }
9685                     color += h;
9686                 }
9687             } else {
9688                 if(v.substr(0, 1) == "#"){
9689                     if(v.length == 4) {
9690                         for(var i = 1; i < 4; i++){
9691                             var c = v.charAt(i);
9692                             color +=  c + c;
9693                         }
9694                     }else if(v.length == 7){
9695                         color += v.substr(1);
9696                     }
9697                 }
9698             }
9699             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9700         },
9701
9702         /**
9703          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9704          * gradient background, rounded corners and a 4-way shadow.
9705          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9706          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9707          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9708          * @return {Roo.Element} this
9709          */
9710         boxWrap : function(cls){
9711             cls = cls || 'x-box';
9712             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9713             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9714             return el;
9715         },
9716
9717         /**
9718          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9719          * @param {String} namespace The namespace in which to look for the attribute
9720          * @param {String} name The attribute name
9721          * @return {String} The attribute value
9722          */
9723         getAttributeNS : Roo.isIE ? function(ns, name){
9724             var d = this.dom;
9725             var type = typeof d[ns+":"+name];
9726             if(type != 'undefined' && type != 'unknown'){
9727                 return d[ns+":"+name];
9728             }
9729             return d[name];
9730         } : function(ns, name){
9731             var d = this.dom;
9732             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9733         },
9734         
9735         
9736         /**
9737          * Sets or Returns the value the dom attribute value
9738          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9739          * @param {String} value (optional) The value to set the attribute to
9740          * @return {String} The attribute value
9741          */
9742         attr : function(name){
9743             if (arguments.length > 1) {
9744                 this.dom.setAttribute(name, arguments[1]);
9745                 return arguments[1];
9746             }
9747             if (typeof(name) == 'object') {
9748                 for(var i in name) {
9749                     this.attr(i, name[i]);
9750                 }
9751                 return name;
9752             }
9753             
9754             
9755             if (!this.dom.hasAttribute(name)) {
9756                 return undefined;
9757             }
9758             return this.dom.getAttribute(name);
9759         }
9760         
9761         
9762         
9763     };
9764
9765     var ep = El.prototype;
9766
9767     /**
9768      * Appends an event handler (Shorthand for addListener)
9769      * @param {String}   eventName     The type of event to append
9770      * @param {Function} fn        The method the event invokes
9771      * @param {Object} scope       (optional) The scope (this object) of the fn
9772      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9773      * @method
9774      */
9775     ep.on = ep.addListener;
9776         // backwards compat
9777     ep.mon = ep.addListener;
9778
9779     /**
9780      * Removes an event handler from this element (shorthand for removeListener)
9781      * @param {String} eventName the type of event to remove
9782      * @param {Function} fn the method the event invokes
9783      * @return {Roo.Element} this
9784      * @method
9785      */
9786     ep.un = ep.removeListener;
9787
9788     /**
9789      * true to automatically adjust width and height settings for box-model issues (default to true)
9790      */
9791     ep.autoBoxAdjust = true;
9792
9793     // private
9794     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9795
9796     // private
9797     El.addUnits = function(v, defaultUnit){
9798         if(v === "" || v == "auto"){
9799             return v;
9800         }
9801         if(v === undefined){
9802             return '';
9803         }
9804         if(typeof v == "number" || !El.unitPattern.test(v)){
9805             return v + (defaultUnit || 'px');
9806         }
9807         return v;
9808     };
9809
9810     // special markup used throughout Roo when box wrapping elements
9811     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>';
9812     /**
9813      * Visibility mode constant - Use visibility to hide element
9814      * @static
9815      * @type Number
9816      */
9817     El.VISIBILITY = 1;
9818     /**
9819      * Visibility mode constant - Use display to hide element
9820      * @static
9821      * @type Number
9822      */
9823     El.DISPLAY = 2;
9824
9825     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9826     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9827     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9828
9829
9830
9831     /**
9832      * @private
9833      */
9834     El.cache = {};
9835
9836     var docEl;
9837
9838     /**
9839      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9840      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9841      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9842      * @return {Element} The Element object
9843      * @static
9844      */
9845     El.get = function(el){
9846         var ex, elm, id;
9847         if(!el){ return null; }
9848         if(typeof el == "string"){ // element id
9849             if(!(elm = document.getElementById(el))){
9850                 return null;
9851             }
9852             if(ex = El.cache[el]){
9853                 ex.dom = elm;
9854             }else{
9855                 ex = El.cache[el] = new El(elm);
9856             }
9857             return ex;
9858         }else if(el.tagName){ // dom element
9859             if(!(id = el.id)){
9860                 id = Roo.id(el);
9861             }
9862             if(ex = El.cache[id]){
9863                 ex.dom = el;
9864             }else{
9865                 ex = El.cache[id] = new El(el);
9866             }
9867             return ex;
9868         }else if(el instanceof El){
9869             if(el != docEl){
9870                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9871                                                               // catch case where it hasn't been appended
9872                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9873             }
9874             return el;
9875         }else if(el.isComposite){
9876             return el;
9877         }else if(el instanceof Array){
9878             return El.select(el);
9879         }else if(el == document){
9880             // create a bogus element object representing the document object
9881             if(!docEl){
9882                 var f = function(){};
9883                 f.prototype = El.prototype;
9884                 docEl = new f();
9885                 docEl.dom = document;
9886             }
9887             return docEl;
9888         }
9889         return null;
9890     };
9891
9892     // private
9893     El.uncache = function(el){
9894         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9895             if(a[i]){
9896                 delete El.cache[a[i].id || a[i]];
9897             }
9898         }
9899     };
9900
9901     // private
9902     // Garbage collection - uncache elements/purge listeners on orphaned elements
9903     // so we don't hold a reference and cause the browser to retain them
9904     El.garbageCollect = function(){
9905         if(!Roo.enableGarbageCollector){
9906             clearInterval(El.collectorThread);
9907             return;
9908         }
9909         for(var eid in El.cache){
9910             var el = El.cache[eid], d = el.dom;
9911             // -------------------------------------------------------
9912             // Determining what is garbage:
9913             // -------------------------------------------------------
9914             // !d
9915             // dom node is null, definitely garbage
9916             // -------------------------------------------------------
9917             // !d.parentNode
9918             // no parentNode == direct orphan, definitely garbage
9919             // -------------------------------------------------------
9920             // !d.offsetParent && !document.getElementById(eid)
9921             // display none elements have no offsetParent so we will
9922             // also try to look it up by it's id. However, check
9923             // offsetParent first so we don't do unneeded lookups.
9924             // This enables collection of elements that are not orphans
9925             // directly, but somewhere up the line they have an orphan
9926             // parent.
9927             // -------------------------------------------------------
9928             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9929                 delete El.cache[eid];
9930                 if(d && Roo.enableListenerCollection){
9931                     E.purgeElement(d);
9932                 }
9933             }
9934         }
9935     }
9936     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9937
9938
9939     // dom is optional
9940     El.Flyweight = function(dom){
9941         this.dom = dom;
9942     };
9943     El.Flyweight.prototype = El.prototype;
9944
9945     El._flyweights = {};
9946     /**
9947      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9948      * the dom node can be overwritten by other code.
9949      * @param {String/HTMLElement} el The dom node or id
9950      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9951      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9952      * @static
9953      * @return {Element} The shared Element object
9954      */
9955     El.fly = function(el, named){
9956         named = named || '_global';
9957         el = Roo.getDom(el);
9958         if(!el){
9959             return null;
9960         }
9961         if(!El._flyweights[named]){
9962             El._flyweights[named] = new El.Flyweight();
9963         }
9964         El._flyweights[named].dom = el;
9965         return El._flyweights[named];
9966     };
9967
9968     /**
9969      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9970      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9971      * Shorthand of {@link Roo.Element#get}
9972      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9973      * @return {Element} The Element object
9974      * @member Roo
9975      * @method get
9976      */
9977     Roo.get = El.get;
9978     /**
9979      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9980      * the dom node can be overwritten by other code.
9981      * Shorthand of {@link Roo.Element#fly}
9982      * @param {String/HTMLElement} el The dom node or id
9983      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9984      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9985      * @static
9986      * @return {Element} The shared Element object
9987      * @member Roo
9988      * @method fly
9989      */
9990     Roo.fly = El.fly;
9991
9992     // speedy lookup for elements never to box adjust
9993     var noBoxAdjust = Roo.isStrict ? {
9994         select:1
9995     } : {
9996         input:1, select:1, textarea:1
9997     };
9998     if(Roo.isIE || Roo.isGecko){
9999         noBoxAdjust['button'] = 1;
10000     }
10001
10002
10003     Roo.EventManager.on(window, 'unload', function(){
10004         delete El.cache;
10005         delete El._flyweights;
10006     });
10007 })();
10008
10009
10010
10011
10012 if(Roo.DomQuery){
10013     Roo.Element.selectorFunction = Roo.DomQuery.select;
10014 }
10015
10016 Roo.Element.select = function(selector, unique, root){
10017     var els;
10018     if(typeof selector == "string"){
10019         els = Roo.Element.selectorFunction(selector, root);
10020     }else if(selector.length !== undefined){
10021         els = selector;
10022     }else{
10023         throw "Invalid selector";
10024     }
10025     if(unique === true){
10026         return new Roo.CompositeElement(els);
10027     }else{
10028         return new Roo.CompositeElementLite(els);
10029     }
10030 };
10031 /**
10032  * Selects elements based on the passed CSS selector to enable working on them as 1.
10033  * @param {String/Array} selector The CSS selector or an array of elements
10034  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10035  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10036  * @return {CompositeElementLite/CompositeElement}
10037  * @member Roo
10038  * @method select
10039  */
10040 Roo.select = Roo.Element.select;
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055 /*
10056  * Based on:
10057  * Ext JS Library 1.1.1
10058  * Copyright(c) 2006-2007, Ext JS, LLC.
10059  *
10060  * Originally Released Under LGPL - original licence link has changed is not relivant.
10061  *
10062  * Fork - LGPL
10063  * <script type="text/javascript">
10064  */
10065
10066
10067
10068 //Notifies Element that fx methods are available
10069 Roo.enableFx = true;
10070
10071 /**
10072  * @class Roo.Fx
10073  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10074  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10075  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10076  * Element effects to work.</p><br/>
10077  *
10078  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10079  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10080  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10081  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10082  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10083  * expected results and should be done with care.</p><br/>
10084  *
10085  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10086  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10087 <pre>
10088 Value  Description
10089 -----  -----------------------------
10090 tl     The top left corner
10091 t      The center of the top edge
10092 tr     The top right corner
10093 l      The center of the left edge
10094 r      The center of the right edge
10095 bl     The bottom left corner
10096 b      The center of the bottom edge
10097 br     The bottom right corner
10098 </pre>
10099  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10100  * below are common options that can be passed to any Fx method.</b>
10101  * @cfg {Function} callback A function called when the effect is finished
10102  * @cfg {Object} scope The scope of the effect function
10103  * @cfg {String} easing A valid Easing value for the effect
10104  * @cfg {String} afterCls A css class to apply after the effect
10105  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10106  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10107  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10108  * effects that end with the element being visually hidden, ignored otherwise)
10109  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10110  * a function which returns such a specification that will be applied to the Element after the effect finishes
10111  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10112  * @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
10113  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10114  */
10115 Roo.Fx = {
10116         /**
10117          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10118          * origin for the slide effect.  This function automatically handles wrapping the element with
10119          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10120          * Usage:
10121          *<pre><code>
10122 // default: slide the element in from the top
10123 el.slideIn();
10124
10125 // custom: slide the element in from the right with a 2-second duration
10126 el.slideIn('r', { duration: 2 });
10127
10128 // common config options shown with default values
10129 el.slideIn('t', {
10130     easing: 'easeOut',
10131     duration: .5
10132 });
10133 </code></pre>
10134          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10135          * @param {Object} options (optional) Object literal with any of the Fx config options
10136          * @return {Roo.Element} The Element
10137          */
10138     slideIn : function(anchor, o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143
10144             anchor = anchor || "t";
10145
10146             // fix display to visibility
10147             this.fixDisplay();
10148
10149             // restore values after effect
10150             var r = this.getFxRestore();
10151             var b = this.getBox();
10152             // fixed size for slide
10153             this.setSize(b);
10154
10155             // wrap if needed
10156             var wrap = this.fxWrap(r.pos, o, "hidden");
10157
10158             var st = this.dom.style;
10159             st.visibility = "visible";
10160             st.position = "absolute";
10161
10162             // clear out temp styles after slide and unwrap
10163             var after = function(){
10164                 el.fxUnwrap(wrap, r.pos, o);
10165                 st.width = r.width;
10166                 st.height = r.height;
10167                 el.afterFx(o);
10168             };
10169             // time to calc the positions
10170             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10171
10172             switch(anchor.toLowerCase()){
10173                 case "t":
10174                     wrap.setSize(b.width, 0);
10175                     st.left = st.bottom = "0";
10176                     a = {height: bh};
10177                 break;
10178                 case "l":
10179                     wrap.setSize(0, b.height);
10180                     st.right = st.top = "0";
10181                     a = {width: bw};
10182                 break;
10183                 case "r":
10184                     wrap.setSize(0, b.height);
10185                     wrap.setX(b.right);
10186                     st.left = st.top = "0";
10187                     a = {width: bw, points: pt};
10188                 break;
10189                 case "b":
10190                     wrap.setSize(b.width, 0);
10191                     wrap.setY(b.bottom);
10192                     st.left = st.top = "0";
10193                     a = {height: bh, points: pt};
10194                 break;
10195                 case "tl":
10196                     wrap.setSize(0, 0);
10197                     st.right = st.bottom = "0";
10198                     a = {width: bw, height: bh};
10199                 break;
10200                 case "bl":
10201                     wrap.setSize(0, 0);
10202                     wrap.setY(b.y+b.height);
10203                     st.right = st.top = "0";
10204                     a = {width: bw, height: bh, points: pt};
10205                 break;
10206                 case "br":
10207                     wrap.setSize(0, 0);
10208                     wrap.setXY([b.right, b.bottom]);
10209                     st.left = st.top = "0";
10210                     a = {width: bw, height: bh, points: pt};
10211                 break;
10212                 case "tr":
10213                     wrap.setSize(0, 0);
10214                     wrap.setX(b.x+b.width);
10215                     st.left = st.bottom = "0";
10216                     a = {width: bw, height: bh, points: pt};
10217                 break;
10218             }
10219             this.dom.style.visibility = "visible";
10220             wrap.show();
10221
10222             arguments.callee.anim = wrap.fxanim(a,
10223                 o,
10224                 'motion',
10225                 .5,
10226                 'easeOut', after);
10227         });
10228         return this;
10229     },
10230     
10231         /**
10232          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10233          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10234          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10235          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10236          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10237          * Usage:
10238          *<pre><code>
10239 // default: slide the element out to the top
10240 el.slideOut();
10241
10242 // custom: slide the element out to the right with a 2-second duration
10243 el.slideOut('r', { duration: 2 });
10244
10245 // common config options shown with default values
10246 el.slideOut('t', {
10247     easing: 'easeOut',
10248     duration: .5,
10249     remove: false,
10250     useDisplay: false
10251 });
10252 </code></pre>
10253          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10254          * @param {Object} options (optional) Object literal with any of the Fx config options
10255          * @return {Roo.Element} The Element
10256          */
10257     slideOut : function(anchor, o){
10258         var el = this.getFxEl();
10259         o = o || {};
10260
10261         el.queueFx(o, function(){
10262
10263             anchor = anchor || "t";
10264
10265             // restore values after effect
10266             var r = this.getFxRestore();
10267             
10268             var b = this.getBox();
10269             // fixed size for slide
10270             this.setSize(b);
10271
10272             // wrap if needed
10273             var wrap = this.fxWrap(r.pos, o, "visible");
10274
10275             var st = this.dom.style;
10276             st.visibility = "visible";
10277             st.position = "absolute";
10278
10279             wrap.setSize(b);
10280
10281             var after = function(){
10282                 if(o.useDisplay){
10283                     el.setDisplayed(false);
10284                 }else{
10285                     el.hide();
10286                 }
10287
10288                 el.fxUnwrap(wrap, r.pos, o);
10289
10290                 st.width = r.width;
10291                 st.height = r.height;
10292
10293                 el.afterFx(o);
10294             };
10295
10296             var a, zero = {to: 0};
10297             switch(anchor.toLowerCase()){
10298                 case "t":
10299                     st.left = st.bottom = "0";
10300                     a = {height: zero};
10301                 break;
10302                 case "l":
10303                     st.right = st.top = "0";
10304                     a = {width: zero};
10305                 break;
10306                 case "r":
10307                     st.left = st.top = "0";
10308                     a = {width: zero, points: {to:[b.right, b.y]}};
10309                 break;
10310                 case "b":
10311                     st.left = st.top = "0";
10312                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10313                 break;
10314                 case "tl":
10315                     st.right = st.bottom = "0";
10316                     a = {width: zero, height: zero};
10317                 break;
10318                 case "bl":
10319                     st.right = st.top = "0";
10320                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10321                 break;
10322                 case "br":
10323                     st.left = st.top = "0";
10324                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10325                 break;
10326                 case "tr":
10327                     st.left = st.bottom = "0";
10328                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10329                 break;
10330             }
10331
10332             arguments.callee.anim = wrap.fxanim(a,
10333                 o,
10334                 'motion',
10335                 .5,
10336                 "easeOut", after);
10337         });
10338         return this;
10339     },
10340
10341         /**
10342          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10343          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10344          * The element must be removed from the DOM using the 'remove' config option if desired.
10345          * Usage:
10346          *<pre><code>
10347 // default
10348 el.puff();
10349
10350 // common config options shown with default values
10351 el.puff({
10352     easing: 'easeOut',
10353     duration: .5,
10354     remove: false,
10355     useDisplay: false
10356 });
10357 </code></pre>
10358          * @param {Object} options (optional) Object literal with any of the Fx config options
10359          * @return {Roo.Element} The Element
10360          */
10361     puff : function(o){
10362         var el = this.getFxEl();
10363         o = o || {};
10364
10365         el.queueFx(o, function(){
10366             this.clearOpacity();
10367             this.show();
10368
10369             // restore values after effect
10370             var r = this.getFxRestore();
10371             var st = this.dom.style;
10372
10373             var after = function(){
10374                 if(o.useDisplay){
10375                     el.setDisplayed(false);
10376                 }else{
10377                     el.hide();
10378                 }
10379
10380                 el.clearOpacity();
10381
10382                 el.setPositioning(r.pos);
10383                 st.width = r.width;
10384                 st.height = r.height;
10385                 st.fontSize = '';
10386                 el.afterFx(o);
10387             };
10388
10389             var width = this.getWidth();
10390             var height = this.getHeight();
10391
10392             arguments.callee.anim = this.fxanim({
10393                     width : {to: this.adjustWidth(width * 2)},
10394                     height : {to: this.adjustHeight(height * 2)},
10395                     points : {by: [-(width * .5), -(height * .5)]},
10396                     opacity : {to: 0},
10397                     fontSize: {to:200, unit: "%"}
10398                 },
10399                 o,
10400                 'motion',
10401                 .5,
10402                 "easeOut", after);
10403         });
10404         return this;
10405     },
10406
10407         /**
10408          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10409          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10410          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10411          * Usage:
10412          *<pre><code>
10413 // default
10414 el.switchOff();
10415
10416 // all config options shown with default values
10417 el.switchOff({
10418     easing: 'easeIn',
10419     duration: .3,
10420     remove: false,
10421     useDisplay: false
10422 });
10423 </code></pre>
10424          * @param {Object} options (optional) Object literal with any of the Fx config options
10425          * @return {Roo.Element} The Element
10426          */
10427     switchOff : function(o){
10428         var el = this.getFxEl();
10429         o = o || {};
10430
10431         el.queueFx(o, function(){
10432             this.clearOpacity();
10433             this.clip();
10434
10435             // restore values after effect
10436             var r = this.getFxRestore();
10437             var st = this.dom.style;
10438
10439             var after = function(){
10440                 if(o.useDisplay){
10441                     el.setDisplayed(false);
10442                 }else{
10443                     el.hide();
10444                 }
10445
10446                 el.clearOpacity();
10447                 el.setPositioning(r.pos);
10448                 st.width = r.width;
10449                 st.height = r.height;
10450
10451                 el.afterFx(o);
10452             };
10453
10454             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10455                 this.clearOpacity();
10456                 (function(){
10457                     this.fxanim({
10458                         height:{to:1},
10459                         points:{by:[0, this.getHeight() * .5]}
10460                     }, o, 'motion', 0.3, 'easeIn', after);
10461                 }).defer(100, this);
10462             });
10463         });
10464         return this;
10465     },
10466
10467     /**
10468      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10469      * changed using the "attr" config option) and then fading back to the original color. If no original
10470      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10471      * Usage:
10472 <pre><code>
10473 // default: highlight background to yellow
10474 el.highlight();
10475
10476 // custom: highlight foreground text to blue for 2 seconds
10477 el.highlight("0000ff", { attr: 'color', duration: 2 });
10478
10479 // common config options shown with default values
10480 el.highlight("ffff9c", {
10481     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10482     endColor: (current color) or "ffffff",
10483     easing: 'easeIn',
10484     duration: 1
10485 });
10486 </code></pre>
10487      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10488      * @param {Object} options (optional) Object literal with any of the Fx config options
10489      * @return {Roo.Element} The Element
10490      */ 
10491     highlight : function(color, o){
10492         var el = this.getFxEl();
10493         o = o || {};
10494
10495         el.queueFx(o, function(){
10496             color = color || "ffff9c";
10497             attr = o.attr || "backgroundColor";
10498
10499             this.clearOpacity();
10500             this.show();
10501
10502             var origColor = this.getColor(attr);
10503             var restoreColor = this.dom.style[attr];
10504             endColor = (o.endColor || origColor) || "ffffff";
10505
10506             var after = function(){
10507                 el.dom.style[attr] = restoreColor;
10508                 el.afterFx(o);
10509             };
10510
10511             var a = {};
10512             a[attr] = {from: color, to: endColor};
10513             arguments.callee.anim = this.fxanim(a,
10514                 o,
10515                 'color',
10516                 1,
10517                 'easeIn', after);
10518         });
10519         return this;
10520     },
10521
10522    /**
10523     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10524     * Usage:
10525 <pre><code>
10526 // default: a single light blue ripple
10527 el.frame();
10528
10529 // custom: 3 red ripples lasting 3 seconds total
10530 el.frame("ff0000", 3, { duration: 3 });
10531
10532 // common config options shown with default values
10533 el.frame("C3DAF9", 1, {
10534     duration: 1 //duration of entire animation (not each individual ripple)
10535     // Note: Easing is not configurable and will be ignored if included
10536 });
10537 </code></pre>
10538     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10539     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10540     * @param {Object} options (optional) Object literal with any of the Fx config options
10541     * @return {Roo.Element} The Element
10542     */
10543     frame : function(color, count, o){
10544         var el = this.getFxEl();
10545         o = o || {};
10546
10547         el.queueFx(o, function(){
10548             color = color || "#C3DAF9";
10549             if(color.length == 6){
10550                 color = "#" + color;
10551             }
10552             count = count || 1;
10553             duration = o.duration || 1;
10554             this.show();
10555
10556             var b = this.getBox();
10557             var animFn = function(){
10558                 var proxy = this.createProxy({
10559
10560                      style:{
10561                         visbility:"hidden",
10562                         position:"absolute",
10563                         "z-index":"35000", // yee haw
10564                         border:"0px solid " + color
10565                      }
10566                   });
10567                 var scale = Roo.isBorderBox ? 2 : 1;
10568                 proxy.animate({
10569                     top:{from:b.y, to:b.y - 20},
10570                     left:{from:b.x, to:b.x - 20},
10571                     borderWidth:{from:0, to:10},
10572                     opacity:{from:1, to:0},
10573                     height:{from:b.height, to:(b.height + (20*scale))},
10574                     width:{from:b.width, to:(b.width + (20*scale))}
10575                 }, duration, function(){
10576                     proxy.remove();
10577                 });
10578                 if(--count > 0){
10579                      animFn.defer((duration/2)*1000, this);
10580                 }else{
10581                     el.afterFx(o);
10582                 }
10583             };
10584             animFn.call(this);
10585         });
10586         return this;
10587     },
10588
10589    /**
10590     * Creates a pause before any subsequent queued effects begin.  If there are
10591     * no effects queued after the pause it will have no effect.
10592     * Usage:
10593 <pre><code>
10594 el.pause(1);
10595 </code></pre>
10596     * @param {Number} seconds The length of time to pause (in seconds)
10597     * @return {Roo.Element} The Element
10598     */
10599     pause : function(seconds){
10600         var el = this.getFxEl();
10601         var o = {};
10602
10603         el.queueFx(o, function(){
10604             setTimeout(function(){
10605                 el.afterFx(o);
10606             }, seconds * 1000);
10607         });
10608         return this;
10609     },
10610
10611    /**
10612     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10613     * using the "endOpacity" config option.
10614     * Usage:
10615 <pre><code>
10616 // default: fade in from opacity 0 to 100%
10617 el.fadeIn();
10618
10619 // custom: fade in from opacity 0 to 75% over 2 seconds
10620 el.fadeIn({ endOpacity: .75, duration: 2});
10621
10622 // common config options shown with default values
10623 el.fadeIn({
10624     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10625     easing: 'easeOut',
10626     duration: .5
10627 });
10628 </code></pre>
10629     * @param {Object} options (optional) Object literal with any of the Fx config options
10630     * @return {Roo.Element} The Element
10631     */
10632     fadeIn : function(o){
10633         var el = this.getFxEl();
10634         o = o || {};
10635         el.queueFx(o, function(){
10636             this.setOpacity(0);
10637             this.fixDisplay();
10638             this.dom.style.visibility = 'visible';
10639             var to = o.endOpacity || 1;
10640             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10641                 o, null, .5, "easeOut", function(){
10642                 if(to == 1){
10643                     this.clearOpacity();
10644                 }
10645                 el.afterFx(o);
10646             });
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade out from the element's current opacity to 0
10657 el.fadeOut();
10658
10659 // custom: fade out from the element's current opacity to 25% over 2 seconds
10660 el.fadeOut({ endOpacity: .25, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeOut({
10664     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667     remove: false,
10668     useDisplay: false
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeOut : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10679                 o, null, .5, "easeOut", function(){
10680                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10681                      this.dom.style.display = "none";
10682                 }else{
10683                      this.dom.style.visibility = "hidden";
10684                 }
10685                 this.clearOpacity();
10686                 el.afterFx(o);
10687             });
10688         });
10689         return this;
10690     },
10691
10692    /**
10693     * Animates the transition of an element's dimensions from a starting height/width
10694     * to an ending height/width.
10695     * Usage:
10696 <pre><code>
10697 // change height and width to 100x100 pixels
10698 el.scale(100, 100);
10699
10700 // common config options shown with default values.  The height and width will default to
10701 // the element's existing values if passed as null.
10702 el.scale(
10703     [element's width],
10704     [element's height], {
10705     easing: 'easeOut',
10706     duration: .35
10707 });
10708 </code></pre>
10709     * @param {Number} width  The new width (pass undefined to keep the original width)
10710     * @param {Number} height  The new height (pass undefined to keep the original height)
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     scale : function(w, h, o){
10715         this.shift(Roo.apply({}, o, {
10716             width: w,
10717             height: h
10718         }));
10719         return this;
10720     },
10721
10722    /**
10723     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10724     * Any of these properties not specified in the config object will not be changed.  This effect 
10725     * requires that at least one new dimension, position or opacity setting must be passed in on
10726     * the config object in order for the function to have any effect.
10727     * Usage:
10728 <pre><code>
10729 // slide the element horizontally to x position 200 while changing the height and opacity
10730 el.shift({ x: 200, height: 50, opacity: .8 });
10731
10732 // common config options shown with default values.
10733 el.shift({
10734     width: [element's width],
10735     height: [element's height],
10736     x: [element's x position],
10737     y: [element's y position],
10738     opacity: [element's opacity],
10739     easing: 'easeOut',
10740     duration: .35
10741 });
10742 </code></pre>
10743     * @param {Object} options  Object literal with any of the Fx config options
10744     * @return {Roo.Element} The Element
10745     */
10746     shift : function(o){
10747         var el = this.getFxEl();
10748         o = o || {};
10749         el.queueFx(o, function(){
10750             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10751             if(w !== undefined){
10752                 a.width = {to: this.adjustWidth(w)};
10753             }
10754             if(h !== undefined){
10755                 a.height = {to: this.adjustHeight(h)};
10756             }
10757             if(x !== undefined || y !== undefined){
10758                 a.points = {to: [
10759                     x !== undefined ? x : this.getX(),
10760                     y !== undefined ? y : this.getY()
10761                 ]};
10762             }
10763             if(op !== undefined){
10764                 a.opacity = {to: op};
10765             }
10766             if(o.xy !== undefined){
10767                 a.points = {to: o.xy};
10768             }
10769             arguments.callee.anim = this.fxanim(a,
10770                 o, 'motion', .35, "easeOut", function(){
10771                 el.afterFx(o);
10772             });
10773         });
10774         return this;
10775     },
10776
10777         /**
10778          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10779          * ending point of the effect.
10780          * Usage:
10781          *<pre><code>
10782 // default: slide the element downward while fading out
10783 el.ghost();
10784
10785 // custom: slide the element out to the right with a 2-second duration
10786 el.ghost('r', { duration: 2 });
10787
10788 // common config options shown with default values
10789 el.ghost('b', {
10790     easing: 'easeOut',
10791     duration: .5
10792     remove: false,
10793     useDisplay: false
10794 });
10795 </code></pre>
10796          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10797          * @param {Object} options (optional) Object literal with any of the Fx config options
10798          * @return {Roo.Element} The Element
10799          */
10800     ghost : function(anchor, o){
10801         var el = this.getFxEl();
10802         o = o || {};
10803
10804         el.queueFx(o, function(){
10805             anchor = anchor || "b";
10806
10807             // restore values after effect
10808             var r = this.getFxRestore();
10809             var w = this.getWidth(),
10810                 h = this.getHeight();
10811
10812             var st = this.dom.style;
10813
10814             var after = function(){
10815                 if(o.useDisplay){
10816                     el.setDisplayed(false);
10817                 }else{
10818                     el.hide();
10819                 }
10820
10821                 el.clearOpacity();
10822                 el.setPositioning(r.pos);
10823                 st.width = r.width;
10824                 st.height = r.height;
10825
10826                 el.afterFx(o);
10827             };
10828
10829             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10830             switch(anchor.toLowerCase()){
10831                 case "t":
10832                     pt.by = [0, -h];
10833                 break;
10834                 case "l":
10835                     pt.by = [-w, 0];
10836                 break;
10837                 case "r":
10838                     pt.by = [w, 0];
10839                 break;
10840                 case "b":
10841                     pt.by = [0, h];
10842                 break;
10843                 case "tl":
10844                     pt.by = [-w, -h];
10845                 break;
10846                 case "bl":
10847                     pt.by = [-w, h];
10848                 break;
10849                 case "br":
10850                     pt.by = [w, h];
10851                 break;
10852                 case "tr":
10853                     pt.by = [w, -h];
10854                 break;
10855             }
10856
10857             arguments.callee.anim = this.fxanim(a,
10858                 o,
10859                 'motion',
10860                 .5,
10861                 "easeOut", after);
10862         });
10863         return this;
10864     },
10865
10866         /**
10867          * Ensures that all effects queued after syncFx is called on the element are
10868          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10869          * @return {Roo.Element} The Element
10870          */
10871     syncFx : function(){
10872         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10873             block : false,
10874             concurrent : true,
10875             stopFx : false
10876         });
10877         return this;
10878     },
10879
10880         /**
10881          * Ensures that all effects queued after sequenceFx is called on the element are
10882          * run in sequence.  This is the opposite of {@link #syncFx}.
10883          * @return {Roo.Element} The Element
10884          */
10885     sequenceFx : function(){
10886         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10887             block : false,
10888             concurrent : false,
10889             stopFx : false
10890         });
10891         return this;
10892     },
10893
10894         /* @private */
10895     nextFx : function(){
10896         var ef = this.fxQueue[0];
10897         if(ef){
10898             ef.call(this);
10899         }
10900     },
10901
10902         /**
10903          * Returns true if the element has any effects actively running or queued, else returns false.
10904          * @return {Boolean} True if element has active effects, else false
10905          */
10906     hasActiveFx : function(){
10907         return this.fxQueue && this.fxQueue[0];
10908     },
10909
10910         /**
10911          * Stops any running effects and clears the element's internal effects queue if it contains
10912          * any additional effects that haven't started yet.
10913          * @return {Roo.Element} The Element
10914          */
10915     stopFx : function(){
10916         if(this.hasActiveFx()){
10917             var cur = this.fxQueue[0];
10918             if(cur && cur.anim && cur.anim.isAnimated()){
10919                 this.fxQueue = [cur]; // clear out others
10920                 cur.anim.stop(true);
10921             }
10922         }
10923         return this;
10924     },
10925
10926         /* @private */
10927     beforeFx : function(o){
10928         if(this.hasActiveFx() && !o.concurrent){
10929            if(o.stopFx){
10930                this.stopFx();
10931                return true;
10932            }
10933            return false;
10934         }
10935         return true;
10936     },
10937
10938         /**
10939          * Returns true if the element is currently blocking so that no other effect can be queued
10940          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10941          * used to ensure that an effect initiated by a user action runs to completion prior to the
10942          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10943          * @return {Boolean} True if blocking, else false
10944          */
10945     hasFxBlock : function(){
10946         var q = this.fxQueue;
10947         return q && q[0] && q[0].block;
10948     },
10949
10950         /* @private */
10951     queueFx : function(o, fn){
10952         if(!this.fxQueue){
10953             this.fxQueue = [];
10954         }
10955         if(!this.hasFxBlock()){
10956             Roo.applyIf(o, this.fxDefaults);
10957             if(!o.concurrent){
10958                 var run = this.beforeFx(o);
10959                 fn.block = o.block;
10960                 this.fxQueue.push(fn);
10961                 if(run){
10962                     this.nextFx();
10963                 }
10964             }else{
10965                 fn.call(this);
10966             }
10967         }
10968         return this;
10969     },
10970
10971         /* @private */
10972     fxWrap : function(pos, o, vis){
10973         var wrap;
10974         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10975             var wrapXY;
10976             if(o.fixPosition){
10977                 wrapXY = this.getXY();
10978             }
10979             var div = document.createElement("div");
10980             div.style.visibility = vis;
10981             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10982             wrap.setPositioning(pos);
10983             if(wrap.getStyle("position") == "static"){
10984                 wrap.position("relative");
10985             }
10986             this.clearPositioning('auto');
10987             wrap.clip();
10988             wrap.dom.appendChild(this.dom);
10989             if(wrapXY){
10990                 wrap.setXY(wrapXY);
10991             }
10992         }
10993         return wrap;
10994     },
10995
10996         /* @private */
10997     fxUnwrap : function(wrap, pos, o){
10998         this.clearPositioning();
10999         this.setPositioning(pos);
11000         if(!o.wrap){
11001             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11002             wrap.remove();
11003         }
11004     },
11005
11006         /* @private */
11007     getFxRestore : function(){
11008         var st = this.dom.style;
11009         return {pos: this.getPositioning(), width: st.width, height : st.height};
11010     },
11011
11012         /* @private */
11013     afterFx : function(o){
11014         if(o.afterStyle){
11015             this.applyStyles(o.afterStyle);
11016         }
11017         if(o.afterCls){
11018             this.addClass(o.afterCls);
11019         }
11020         if(o.remove === true){
11021             this.remove();
11022         }
11023         Roo.callback(o.callback, o.scope, [this]);
11024         if(!o.concurrent){
11025             this.fxQueue.shift();
11026             this.nextFx();
11027         }
11028     },
11029
11030         /* @private */
11031     getFxEl : function(){ // support for composite element fx
11032         return Roo.get(this.dom);
11033     },
11034
11035         /* @private */
11036     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11037         animType = animType || 'run';
11038         opt = opt || {};
11039         var anim = Roo.lib.Anim[animType](
11040             this.dom, args,
11041             (opt.duration || defaultDur) || .35,
11042             (opt.easing || defaultEase) || 'easeOut',
11043             function(){
11044                 Roo.callback(cb, this);
11045             },
11046             this
11047         );
11048         opt.anim = anim;
11049         return anim;
11050     }
11051 };
11052
11053 // backwords compat
11054 Roo.Fx.resize = Roo.Fx.scale;
11055
11056 //When included, Roo.Fx is automatically applied to Element so that all basic
11057 //effects are available directly via the Element API
11058 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11059  * Based on:
11060  * Ext JS Library 1.1.1
11061  * Copyright(c) 2006-2007, Ext JS, LLC.
11062  *
11063  * Originally Released Under LGPL - original licence link has changed is not relivant.
11064  *
11065  * Fork - LGPL
11066  * <script type="text/javascript">
11067  */
11068
11069
11070 /**
11071  * @class Roo.CompositeElement
11072  * Standard composite class. Creates a Roo.Element for every element in the collection.
11073  * <br><br>
11074  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11075  * actions will be performed on all the elements in this collection.</b>
11076  * <br><br>
11077  * All methods return <i>this</i> and can be chained.
11078  <pre><code>
11079  var els = Roo.select("#some-el div.some-class", true);
11080  // or select directly from an existing element
11081  var el = Roo.get('some-el');
11082  el.select('div.some-class', true);
11083
11084  els.setWidth(100); // all elements become 100 width
11085  els.hide(true); // all elements fade out and hide
11086  // or
11087  els.setWidth(100).hide(true);
11088  </code></pre>
11089  */
11090 Roo.CompositeElement = function(els){
11091     this.elements = [];
11092     this.addElements(els);
11093 };
11094 Roo.CompositeElement.prototype = {
11095     isComposite: true,
11096     addElements : function(els){
11097         if(!els) {
11098             return this;
11099         }
11100         if(typeof els == "string"){
11101             els = Roo.Element.selectorFunction(els);
11102         }
11103         var yels = this.elements;
11104         var index = yels.length-1;
11105         for(var i = 0, len = els.length; i < len; i++) {
11106                 yels[++index] = Roo.get(els[i]);
11107         }
11108         return this;
11109     },
11110
11111     /**
11112     * Clears this composite and adds the elements returned by the passed selector.
11113     * @param {String/Array} els A string CSS selector, an array of elements or an element
11114     * @return {CompositeElement} this
11115     */
11116     fill : function(els){
11117         this.elements = [];
11118         this.add(els);
11119         return this;
11120     },
11121
11122     /**
11123     * Filters this composite to only elements that match the passed selector.
11124     * @param {String} selector A string CSS selector
11125     * @param {Boolean} inverse return inverse filter (not matches)
11126     * @return {CompositeElement} this
11127     */
11128     filter : function(selector, inverse){
11129         var els = [];
11130         inverse = inverse || false;
11131         this.each(function(el){
11132             var match = inverse ? !el.is(selector) : el.is(selector);
11133             if(match){
11134                 els[els.length] = el.dom;
11135             }
11136         });
11137         this.fill(els);
11138         return this;
11139     },
11140
11141     invoke : function(fn, args){
11142         var els = this.elements;
11143         for(var i = 0, len = els.length; i < len; i++) {
11144                 Roo.Element.prototype[fn].apply(els[i], args);
11145         }
11146         return this;
11147     },
11148     /**
11149     * Adds elements to this composite.
11150     * @param {String/Array} els A string CSS selector, an array of elements or an element
11151     * @return {CompositeElement} this
11152     */
11153     add : function(els){
11154         if(typeof els == "string"){
11155             this.addElements(Roo.Element.selectorFunction(els));
11156         }else if(els.length !== undefined){
11157             this.addElements(els);
11158         }else{
11159             this.addElements([els]);
11160         }
11161         return this;
11162     },
11163     /**
11164     * Calls the passed function passing (el, this, index) for each element in this composite.
11165     * @param {Function} fn The function to call
11166     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11167     * @return {CompositeElement} this
11168     */
11169     each : function(fn, scope){
11170         var els = this.elements;
11171         for(var i = 0, len = els.length; i < len; i++){
11172             if(fn.call(scope || els[i], els[i], this, i) === false) {
11173                 break;
11174             }
11175         }
11176         return this;
11177     },
11178
11179     /**
11180      * Returns the Element object at the specified index
11181      * @param {Number} index
11182      * @return {Roo.Element}
11183      */
11184     item : function(index){
11185         return this.elements[index] || null;
11186     },
11187
11188     /**
11189      * Returns the first Element
11190      * @return {Roo.Element}
11191      */
11192     first : function(){
11193         return this.item(0);
11194     },
11195
11196     /**
11197      * Returns the last Element
11198      * @return {Roo.Element}
11199      */
11200     last : function(){
11201         return this.item(this.elements.length-1);
11202     },
11203
11204     /**
11205      * Returns the number of elements in this composite
11206      * @return Number
11207      */
11208     getCount : function(){
11209         return this.elements.length;
11210     },
11211
11212     /**
11213      * Returns true if this composite contains the passed element
11214      * @return Boolean
11215      */
11216     contains : function(el){
11217         return this.indexOf(el) !== -1;
11218     },
11219
11220     /**
11221      * Returns true if this composite contains the passed element
11222      * @return Boolean
11223      */
11224     indexOf : function(el){
11225         return this.elements.indexOf(Roo.get(el));
11226     },
11227
11228
11229     /**
11230     * Removes the specified element(s).
11231     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11232     * or an array of any of those.
11233     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11234     * @return {CompositeElement} this
11235     */
11236     removeElement : function(el, removeDom){
11237         if(el instanceof Array){
11238             for(var i = 0, len = el.length; i < len; i++){
11239                 this.removeElement(el[i]);
11240             }
11241             return this;
11242         }
11243         var index = typeof el == 'number' ? el : this.indexOf(el);
11244         if(index !== -1){
11245             if(removeDom){
11246                 var d = this.elements[index];
11247                 if(d.dom){
11248                     d.remove();
11249                 }else{
11250                     d.parentNode.removeChild(d);
11251                 }
11252             }
11253             this.elements.splice(index, 1);
11254         }
11255         return this;
11256     },
11257
11258     /**
11259     * Replaces the specified element with the passed element.
11260     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11261     * to replace.
11262     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11263     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11264     * @return {CompositeElement} this
11265     */
11266     replaceElement : function(el, replacement, domReplace){
11267         var index = typeof el == 'number' ? el : this.indexOf(el);
11268         if(index !== -1){
11269             if(domReplace){
11270                 this.elements[index].replaceWith(replacement);
11271             }else{
11272                 this.elements.splice(index, 1, Roo.get(replacement))
11273             }
11274         }
11275         return this;
11276     },
11277
11278     /**
11279      * Removes all elements.
11280      */
11281     clear : function(){
11282         this.elements = [];
11283     }
11284 };
11285 (function(){
11286     Roo.CompositeElement.createCall = function(proto, fnName){
11287         if(!proto[fnName]){
11288             proto[fnName] = function(){
11289                 return this.invoke(fnName, arguments);
11290             };
11291         }
11292     };
11293     for(var fnName in Roo.Element.prototype){
11294         if(typeof Roo.Element.prototype[fnName] == "function"){
11295             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11296         }
11297     };
11298 })();
11299 /*
11300  * Based on:
11301  * Ext JS Library 1.1.1
11302  * Copyright(c) 2006-2007, Ext JS, LLC.
11303  *
11304  * Originally Released Under LGPL - original licence link has changed is not relivant.
11305  *
11306  * Fork - LGPL
11307  * <script type="text/javascript">
11308  */
11309
11310 /**
11311  * @class Roo.CompositeElementLite
11312  * @extends Roo.CompositeElement
11313  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11314  <pre><code>
11315  var els = Roo.select("#some-el div.some-class");
11316  // or select directly from an existing element
11317  var el = Roo.get('some-el');
11318  el.select('div.some-class');
11319
11320  els.setWidth(100); // all elements become 100 width
11321  els.hide(true); // all elements fade out and hide
11322  // or
11323  els.setWidth(100).hide(true);
11324  </code></pre><br><br>
11325  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11326  * actions will be performed on all the elements in this collection.</b>
11327  */
11328 Roo.CompositeElementLite = function(els){
11329     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11330     this.el = new Roo.Element.Flyweight();
11331 };
11332 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11333     addElements : function(els){
11334         if(els){
11335             if(els instanceof Array){
11336                 this.elements = this.elements.concat(els);
11337             }else{
11338                 var yels = this.elements;
11339                 var index = yels.length-1;
11340                 for(var i = 0, len = els.length; i < len; i++) {
11341                     yels[++index] = els[i];
11342                 }
11343             }
11344         }
11345         return this;
11346     },
11347     invoke : function(fn, args){
11348         var els = this.elements;
11349         var el = this.el;
11350         for(var i = 0, len = els.length; i < len; i++) {
11351             el.dom = els[i];
11352                 Roo.Element.prototype[fn].apply(el, args);
11353         }
11354         return this;
11355     },
11356     /**
11357      * Returns a flyweight Element of the dom element object at the specified index
11358      * @param {Number} index
11359      * @return {Roo.Element}
11360      */
11361     item : function(index){
11362         if(!this.elements[index]){
11363             return null;
11364         }
11365         this.el.dom = this.elements[index];
11366         return this.el;
11367     },
11368
11369     // fixes scope with flyweight
11370     addListener : function(eventName, handler, scope, opt){
11371         var els = this.elements;
11372         for(var i = 0, len = els.length; i < len; i++) {
11373             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11374         }
11375         return this;
11376     },
11377
11378     /**
11379     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11380     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11381     * a reference to the dom node, use el.dom.</b>
11382     * @param {Function} fn The function to call
11383     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11384     * @return {CompositeElement} this
11385     */
11386     each : function(fn, scope){
11387         var els = this.elements;
11388         var el = this.el;
11389         for(var i = 0, len = els.length; i < len; i++){
11390             el.dom = els[i];
11391                 if(fn.call(scope || el, el, this, i) === false){
11392                 break;
11393             }
11394         }
11395         return this;
11396     },
11397
11398     indexOf : function(el){
11399         return this.elements.indexOf(Roo.getDom(el));
11400     },
11401
11402     replaceElement : function(el, replacement, domReplace){
11403         var index = typeof el == 'number' ? el : this.indexOf(el);
11404         if(index !== -1){
11405             replacement = Roo.getDom(replacement);
11406             if(domReplace){
11407                 var d = this.elements[index];
11408                 d.parentNode.insertBefore(replacement, d);
11409                 d.parentNode.removeChild(d);
11410             }
11411             this.elements.splice(index, 1, replacement);
11412         }
11413         return this;
11414     }
11415 });
11416 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11417
11418 /*
11419  * Based on:
11420  * Ext JS Library 1.1.1
11421  * Copyright(c) 2006-2007, Ext JS, LLC.
11422  *
11423  * Originally Released Under LGPL - original licence link has changed is not relivant.
11424  *
11425  * Fork - LGPL
11426  * <script type="text/javascript">
11427  */
11428
11429  
11430
11431 /**
11432  * @class Roo.data.Connection
11433  * @extends Roo.util.Observable
11434  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11435  * either to a configured URL, or to a URL specified at request time.<br><br>
11436  * <p>
11437  * Requests made by this class are asynchronous, and will return immediately. No data from
11438  * the server will be available to the statement immediately following the {@link #request} call.
11439  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11440  * <p>
11441  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11442  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11443  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11444  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11445  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11446  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11447  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11448  * standard DOM methods.
11449  * @constructor
11450  * @param {Object} config a configuration object.
11451  */
11452 Roo.data.Connection = function(config){
11453     Roo.apply(this, config);
11454     this.addEvents({
11455         /**
11456          * @event beforerequest
11457          * Fires before a network request is made to retrieve a data object.
11458          * @param {Connection} conn This Connection object.
11459          * @param {Object} options The options config object passed to the {@link #request} method.
11460          */
11461         "beforerequest" : true,
11462         /**
11463          * @event requestcomplete
11464          * Fires if the request was successfully completed.
11465          * @param {Connection} conn This Connection object.
11466          * @param {Object} response The XHR object containing the response data.
11467          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11468          * @param {Object} options The options config object passed to the {@link #request} method.
11469          */
11470         "requestcomplete" : true,
11471         /**
11472          * @event requestexception
11473          * Fires if an error HTTP status was returned from the server.
11474          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11475          * @param {Connection} conn This Connection object.
11476          * @param {Object} response The XHR object containing the response data.
11477          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11478          * @param {Object} options The options config object passed to the {@link #request} method.
11479          */
11480         "requestexception" : true
11481     });
11482     Roo.data.Connection.superclass.constructor.call(this);
11483 };
11484
11485 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11486     /**
11487      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11488      */
11489     /**
11490      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11491      * extra parameters to each request made by this object. (defaults to undefined)
11492      */
11493     /**
11494      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11495      *  to each request made by this object. (defaults to undefined)
11496      */
11497     /**
11498      * @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)
11499      */
11500     /**
11501      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11502      */
11503     timeout : 30000,
11504     /**
11505      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11506      * @type Boolean
11507      */
11508     autoAbort:false,
11509
11510     /**
11511      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11512      * @type Boolean
11513      */
11514     disableCaching: true,
11515
11516     /**
11517      * Sends an HTTP request to a remote server.
11518      * @param {Object} options An object which may contain the following properties:<ul>
11519      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11520      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11521      * request, a url encoded string or a function to call to get either.</li>
11522      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11523      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11524      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11525      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11526      * <li>options {Object} The parameter to the request call.</li>
11527      * <li>success {Boolean} True if the request succeeded.</li>
11528      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11529      * </ul></li>
11530      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11531      * The callback is passed the following parameters:<ul>
11532      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11533      * <li>options {Object} The parameter to the request call.</li>
11534      * </ul></li>
11535      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11536      * The callback is passed the following parameters:<ul>
11537      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11538      * <li>options {Object} The parameter to the request call.</li>
11539      * </ul></li>
11540      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11541      * for the callback function. Defaults to the browser window.</li>
11542      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11543      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11544      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11545      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11546      * params for the post data. Any params will be appended to the URL.</li>
11547      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11548      * </ul>
11549      * @return {Number} transactionId
11550      */
11551     request : function(o){
11552         if(this.fireEvent("beforerequest", this, o) !== false){
11553             var p = o.params;
11554
11555             if(typeof p == "function"){
11556                 p = p.call(o.scope||window, o);
11557             }
11558             if(typeof p == "object"){
11559                 p = Roo.urlEncode(o.params);
11560             }
11561             if(this.extraParams){
11562                 var extras = Roo.urlEncode(this.extraParams);
11563                 p = p ? (p + '&' + extras) : extras;
11564             }
11565
11566             var url = o.url || this.url;
11567             if(typeof url == 'function'){
11568                 url = url.call(o.scope||window, o);
11569             }
11570
11571             if(o.form){
11572                 var form = Roo.getDom(o.form);
11573                 url = url || form.action;
11574
11575                 var enctype = form.getAttribute("enctype");
11576                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11577                     return this.doFormUpload(o, p, url);
11578                 }
11579                 var f = Roo.lib.Ajax.serializeForm(form);
11580                 p = p ? (p + '&' + f) : f;
11581             }
11582
11583             var hs = o.headers;
11584             if(this.defaultHeaders){
11585                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11586                 if(!o.headers){
11587                     o.headers = hs;
11588                 }
11589             }
11590
11591             var cb = {
11592                 success: this.handleResponse,
11593                 failure: this.handleFailure,
11594                 scope: this,
11595                 argument: {options: o},
11596                 timeout : o.timeout || this.timeout
11597             };
11598
11599             var method = o.method||this.method||(p ? "POST" : "GET");
11600
11601             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11602                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11603             }
11604
11605             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11606                 if(o.autoAbort){
11607                     this.abort();
11608                 }
11609             }else if(this.autoAbort !== false){
11610                 this.abort();
11611             }
11612
11613             if((method == 'GET' && p) || o.xmlData){
11614                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11615                 p = '';
11616             }
11617             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11618             return this.transId;
11619         }else{
11620             Roo.callback(o.callback, o.scope, [o, null, null]);
11621             return null;
11622         }
11623     },
11624
11625     /**
11626      * Determine whether this object has a request outstanding.
11627      * @param {Number} transactionId (Optional) defaults to the last transaction
11628      * @return {Boolean} True if there is an outstanding request.
11629      */
11630     isLoading : function(transId){
11631         if(transId){
11632             return Roo.lib.Ajax.isCallInProgress(transId);
11633         }else{
11634             return this.transId ? true : false;
11635         }
11636     },
11637
11638     /**
11639      * Aborts any outstanding request.
11640      * @param {Number} transactionId (Optional) defaults to the last transaction
11641      */
11642     abort : function(transId){
11643         if(transId || this.isLoading()){
11644             Roo.lib.Ajax.abort(transId || this.transId);
11645         }
11646     },
11647
11648     // private
11649     handleResponse : function(response){
11650         this.transId = false;
11651         var options = response.argument.options;
11652         response.argument = options ? options.argument : null;
11653         this.fireEvent("requestcomplete", this, response, options);
11654         Roo.callback(options.success, options.scope, [response, options]);
11655         Roo.callback(options.callback, options.scope, [options, true, response]);
11656     },
11657
11658     // private
11659     handleFailure : function(response, e){
11660         this.transId = false;
11661         var options = response.argument.options;
11662         response.argument = options ? options.argument : null;
11663         this.fireEvent("requestexception", this, response, options, e);
11664         Roo.callback(options.failure, options.scope, [response, options]);
11665         Roo.callback(options.callback, options.scope, [options, false, response]);
11666     },
11667
11668     // private
11669     doFormUpload : function(o, ps, url){
11670         var id = Roo.id();
11671         var frame = document.createElement('iframe');
11672         frame.id = id;
11673         frame.name = id;
11674         frame.className = 'x-hidden';
11675         if(Roo.isIE){
11676             frame.src = Roo.SSL_SECURE_URL;
11677         }
11678         document.body.appendChild(frame);
11679
11680         if(Roo.isIE){
11681            document.frames[id].name = id;
11682         }
11683
11684         var form = Roo.getDom(o.form);
11685         form.target = id;
11686         form.method = 'POST';
11687         form.enctype = form.encoding = 'multipart/form-data';
11688         if(url){
11689             form.action = url;
11690         }
11691
11692         var hiddens, hd;
11693         if(ps){ // add dynamic params
11694             hiddens = [];
11695             ps = Roo.urlDecode(ps, false);
11696             for(var k in ps){
11697                 if(ps.hasOwnProperty(k)){
11698                     hd = document.createElement('input');
11699                     hd.type = 'hidden';
11700                     hd.name = k;
11701                     hd.value = ps[k];
11702                     form.appendChild(hd);
11703                     hiddens.push(hd);
11704                 }
11705             }
11706         }
11707
11708         function cb(){
11709             var r = {  // bogus response object
11710                 responseText : '',
11711                 responseXML : null
11712             };
11713
11714             r.argument = o ? o.argument : null;
11715
11716             try { //
11717                 var doc;
11718                 if(Roo.isIE){
11719                     doc = frame.contentWindow.document;
11720                 }else {
11721                     doc = (frame.contentDocument || window.frames[id].document);
11722                 }
11723                 if(doc && doc.body){
11724                     r.responseText = doc.body.innerHTML;
11725                 }
11726                 if(doc && doc.XMLDocument){
11727                     r.responseXML = doc.XMLDocument;
11728                 }else {
11729                     r.responseXML = doc;
11730                 }
11731             }
11732             catch(e) {
11733                 // ignore
11734             }
11735
11736             Roo.EventManager.removeListener(frame, 'load', cb, this);
11737
11738             this.fireEvent("requestcomplete", this, r, o);
11739             Roo.callback(o.success, o.scope, [r, o]);
11740             Roo.callback(o.callback, o.scope, [o, true, r]);
11741
11742             setTimeout(function(){document.body.removeChild(frame);}, 100);
11743         }
11744
11745         Roo.EventManager.on(frame, 'load', cb, this);
11746         form.submit();
11747
11748         if(hiddens){ // remove dynamic params
11749             for(var i = 0, len = hiddens.length; i < len; i++){
11750                 form.removeChild(hiddens[i]);
11751             }
11752         }
11753     }
11754 });
11755 /*
11756  * Based on:
11757  * Ext JS Library 1.1.1
11758  * Copyright(c) 2006-2007, Ext JS, LLC.
11759  *
11760  * Originally Released Under LGPL - original licence link has changed is not relivant.
11761  *
11762  * Fork - LGPL
11763  * <script type="text/javascript">
11764  */
11765  
11766 /**
11767  * Global Ajax request class.
11768  * 
11769  * @class Roo.Ajax
11770  * @extends Roo.data.Connection
11771  * @static
11772  * 
11773  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11774  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11775  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11776  * @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)
11777  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11778  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11779  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11780  */
11781 Roo.Ajax = new Roo.data.Connection({
11782     // fix up the docs
11783     /**
11784      * @scope Roo.Ajax
11785      * @type {Boolear} 
11786      */
11787     autoAbort : false,
11788
11789     /**
11790      * Serialize the passed form into a url encoded string
11791      * @scope Roo.Ajax
11792      * @param {String/HTMLElement} form
11793      * @return {String}
11794      */
11795     serializeForm : function(form){
11796         return Roo.lib.Ajax.serializeForm(form);
11797     }
11798 });/*
11799  * Based on:
11800  * Ext JS Library 1.1.1
11801  * Copyright(c) 2006-2007, Ext JS, LLC.
11802  *
11803  * Originally Released Under LGPL - original licence link has changed is not relivant.
11804  *
11805  * Fork - LGPL
11806  * <script type="text/javascript">
11807  */
11808
11809  
11810 /**
11811  * @class Roo.UpdateManager
11812  * @extends Roo.util.Observable
11813  * Provides AJAX-style update for Element object.<br><br>
11814  * Usage:<br>
11815  * <pre><code>
11816  * // Get it from a Roo.Element object
11817  * var el = Roo.get("foo");
11818  * var mgr = el.getUpdateManager();
11819  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11820  * ...
11821  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11822  * <br>
11823  * // or directly (returns the same UpdateManager instance)
11824  * var mgr = new Roo.UpdateManager("myElementId");
11825  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11826  * mgr.on("update", myFcnNeedsToKnow);
11827  * <br>
11828    // short handed call directly from the element object
11829    Roo.get("foo").load({
11830         url: "bar.php",
11831         scripts:true,
11832         params: "for=bar",
11833         text: "Loading Foo..."
11834    });
11835  * </code></pre>
11836  * @constructor
11837  * Create new UpdateManager directly.
11838  * @param {String/HTMLElement/Roo.Element} el The element to update
11839  * @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).
11840  */
11841 Roo.UpdateManager = function(el, forceNew){
11842     el = Roo.get(el);
11843     if(!forceNew && el.updateManager){
11844         return el.updateManager;
11845     }
11846     /**
11847      * The Element object
11848      * @type Roo.Element
11849      */
11850     this.el = el;
11851     /**
11852      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11853      * @type String
11854      */
11855     this.defaultUrl = null;
11856
11857     this.addEvents({
11858         /**
11859          * @event beforeupdate
11860          * Fired before an update is made, return false from your handler and the update is cancelled.
11861          * @param {Roo.Element} el
11862          * @param {String/Object/Function} url
11863          * @param {String/Object} params
11864          */
11865         "beforeupdate": true,
11866         /**
11867          * @event update
11868          * Fired after successful update is made.
11869          * @param {Roo.Element} el
11870          * @param {Object} oResponseObject The response Object
11871          */
11872         "update": true,
11873         /**
11874          * @event failure
11875          * Fired on update failure.
11876          * @param {Roo.Element} el
11877          * @param {Object} oResponseObject The response Object
11878          */
11879         "failure": true
11880     });
11881     var d = Roo.UpdateManager.defaults;
11882     /**
11883      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11884      * @type String
11885      */
11886     this.sslBlankUrl = d.sslBlankUrl;
11887     /**
11888      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11889      * @type Boolean
11890      */
11891     this.disableCaching = d.disableCaching;
11892     /**
11893      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11894      * @type String
11895      */
11896     this.indicatorText = d.indicatorText;
11897     /**
11898      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11899      * @type String
11900      */
11901     this.showLoadIndicator = d.showLoadIndicator;
11902     /**
11903      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11904      * @type Number
11905      */
11906     this.timeout = d.timeout;
11907
11908     /**
11909      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11910      * @type Boolean
11911      */
11912     this.loadScripts = d.loadScripts;
11913
11914     /**
11915      * Transaction object of current executing transaction
11916      */
11917     this.transaction = null;
11918
11919     /**
11920      * @private
11921      */
11922     this.autoRefreshProcId = null;
11923     /**
11924      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11925      * @type Function
11926      */
11927     this.refreshDelegate = this.refresh.createDelegate(this);
11928     /**
11929      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11930      * @type Function
11931      */
11932     this.updateDelegate = this.update.createDelegate(this);
11933     /**
11934      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11935      * @type Function
11936      */
11937     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11938     /**
11939      * @private
11940      */
11941     this.successDelegate = this.processSuccess.createDelegate(this);
11942     /**
11943      * @private
11944      */
11945     this.failureDelegate = this.processFailure.createDelegate(this);
11946
11947     if(!this.renderer){
11948      /**
11949       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11950       */
11951     this.renderer = new Roo.UpdateManager.BasicRenderer();
11952     }
11953     
11954     Roo.UpdateManager.superclass.constructor.call(this);
11955 };
11956
11957 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11958     /**
11959      * Get the Element this UpdateManager is bound to
11960      * @return {Roo.Element} The element
11961      */
11962     getEl : function(){
11963         return this.el;
11964     },
11965     /**
11966      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11967      * @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:
11968 <pre><code>
11969 um.update({<br/>
11970     url: "your-url.php",<br/>
11971     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11972     callback: yourFunction,<br/>
11973     scope: yourObject, //(optional scope)  <br/>
11974     discardUrl: false, <br/>
11975     nocache: false,<br/>
11976     text: "Loading...",<br/>
11977     timeout: 30,<br/>
11978     scripts: false<br/>
11979 });
11980 </code></pre>
11981      * The only required property is url. The optional properties nocache, text and scripts
11982      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11983      * @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}
11984      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11985      * @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.
11986      */
11987     update : function(url, params, callback, discardUrl){
11988         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11989             var method = this.method,
11990                 cfg;
11991             if(typeof url == "object"){ // must be config object
11992                 cfg = url;
11993                 url = cfg.url;
11994                 params = params || cfg.params;
11995                 callback = callback || cfg.callback;
11996                 discardUrl = discardUrl || cfg.discardUrl;
11997                 if(callback && cfg.scope){
11998                     callback = callback.createDelegate(cfg.scope);
11999                 }
12000                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12001                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12002                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12003                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12004                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12005             }
12006             this.showLoading();
12007             if(!discardUrl){
12008                 this.defaultUrl = url;
12009             }
12010             if(typeof url == "function"){
12011                 url = url.call(this);
12012             }
12013
12014             method = method || (params ? "POST" : "GET");
12015             if(method == "GET"){
12016                 url = this.prepareUrl(url);
12017             }
12018
12019             var o = Roo.apply(cfg ||{}, {
12020                 url : url,
12021                 params: params,
12022                 success: this.successDelegate,
12023                 failure: this.failureDelegate,
12024                 callback: undefined,
12025                 timeout: (this.timeout*1000),
12026                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12027             });
12028             Roo.log("updated manager called with timeout of " + o.timeout);
12029             this.transaction = Roo.Ajax.request(o);
12030         }
12031     },
12032
12033     /**
12034      * 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.
12035      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12036      * @param {String/HTMLElement} form The form Id or form element
12037      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12038      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12039      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12040      */
12041     formUpdate : function(form, url, reset, callback){
12042         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12043             if(typeof url == "function"){
12044                 url = url.call(this);
12045             }
12046             form = Roo.getDom(form);
12047             this.transaction = Roo.Ajax.request({
12048                 form: form,
12049                 url:url,
12050                 success: this.successDelegate,
12051                 failure: this.failureDelegate,
12052                 timeout: (this.timeout*1000),
12053                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12054             });
12055             this.showLoading.defer(1, this);
12056         }
12057     },
12058
12059     /**
12060      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12061      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12062      */
12063     refresh : function(callback){
12064         if(this.defaultUrl == null){
12065             return;
12066         }
12067         this.update(this.defaultUrl, null, callback, true);
12068     },
12069
12070     /**
12071      * Set this element to auto refresh.
12072      * @param {Number} interval How often to update (in seconds).
12073      * @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)
12074      * @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}
12075      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12076      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12077      */
12078     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12079         if(refreshNow){
12080             this.update(url || this.defaultUrl, params, callback, true);
12081         }
12082         if(this.autoRefreshProcId){
12083             clearInterval(this.autoRefreshProcId);
12084         }
12085         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12086     },
12087
12088     /**
12089      * Stop auto refresh on this element.
12090      */
12091      stopAutoRefresh : function(){
12092         if(this.autoRefreshProcId){
12093             clearInterval(this.autoRefreshProcId);
12094             delete this.autoRefreshProcId;
12095         }
12096     },
12097
12098     isAutoRefreshing : function(){
12099        return this.autoRefreshProcId ? true : false;
12100     },
12101     /**
12102      * Called to update the element to "Loading" state. Override to perform custom action.
12103      */
12104     showLoading : function(){
12105         if(this.showLoadIndicator){
12106             this.el.update(this.indicatorText);
12107         }
12108     },
12109
12110     /**
12111      * Adds unique parameter to query string if disableCaching = true
12112      * @private
12113      */
12114     prepareUrl : function(url){
12115         if(this.disableCaching){
12116             var append = "_dc=" + (new Date().getTime());
12117             if(url.indexOf("?") !== -1){
12118                 url += "&" + append;
12119             }else{
12120                 url += "?" + append;
12121             }
12122         }
12123         return url;
12124     },
12125
12126     /**
12127      * @private
12128      */
12129     processSuccess : function(response){
12130         this.transaction = null;
12131         if(response.argument.form && response.argument.reset){
12132             try{ // put in try/catch since some older FF releases had problems with this
12133                 response.argument.form.reset();
12134             }catch(e){}
12135         }
12136         if(this.loadScripts){
12137             this.renderer.render(this.el, response, this,
12138                 this.updateComplete.createDelegate(this, [response]));
12139         }else{
12140             this.renderer.render(this.el, response, this);
12141             this.updateComplete(response);
12142         }
12143     },
12144
12145     updateComplete : function(response){
12146         this.fireEvent("update", this.el, response);
12147         if(typeof response.argument.callback == "function"){
12148             response.argument.callback(this.el, true, response);
12149         }
12150     },
12151
12152     /**
12153      * @private
12154      */
12155     processFailure : function(response){
12156         this.transaction = null;
12157         this.fireEvent("failure", this.el, response);
12158         if(typeof response.argument.callback == "function"){
12159             response.argument.callback(this.el, false, response);
12160         }
12161     },
12162
12163     /**
12164      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12165      * @param {Object} renderer The object implementing the render() method
12166      */
12167     setRenderer : function(renderer){
12168         this.renderer = renderer;
12169     },
12170
12171     getRenderer : function(){
12172        return this.renderer;
12173     },
12174
12175     /**
12176      * Set the defaultUrl used for updates
12177      * @param {String/Function} defaultUrl The url or a function to call to get the url
12178      */
12179     setDefaultUrl : function(defaultUrl){
12180         this.defaultUrl = defaultUrl;
12181     },
12182
12183     /**
12184      * Aborts the executing transaction
12185      */
12186     abort : function(){
12187         if(this.transaction){
12188             Roo.Ajax.abort(this.transaction);
12189         }
12190     },
12191
12192     /**
12193      * Returns true if an update is in progress
12194      * @return {Boolean}
12195      */
12196     isUpdating : function(){
12197         if(this.transaction){
12198             return Roo.Ajax.isLoading(this.transaction);
12199         }
12200         return false;
12201     }
12202 });
12203
12204 /**
12205  * @class Roo.UpdateManager.defaults
12206  * @static (not really - but it helps the doc tool)
12207  * The defaults collection enables customizing the default properties of UpdateManager
12208  */
12209    Roo.UpdateManager.defaults = {
12210        /**
12211          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12212          * @type Number
12213          */
12214          timeout : 30,
12215
12216          /**
12217          * True to process scripts by default (Defaults to false).
12218          * @type Boolean
12219          */
12220         loadScripts : false,
12221
12222         /**
12223         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12224         * @type String
12225         */
12226         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12227         /**
12228          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12229          * @type Boolean
12230          */
12231         disableCaching : false,
12232         /**
12233          * Whether to show indicatorText when loading (Defaults to true).
12234          * @type Boolean
12235          */
12236         showLoadIndicator : true,
12237         /**
12238          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12239          * @type String
12240          */
12241         indicatorText : '<div class="loading-indicator">Loading...</div>'
12242    };
12243
12244 /**
12245  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12246  *Usage:
12247  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12248  * @param {String/HTMLElement/Roo.Element} el The element to update
12249  * @param {String} url The url
12250  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12251  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12252  * @static
12253  * @deprecated
12254  * @member Roo.UpdateManager
12255  */
12256 Roo.UpdateManager.updateElement = function(el, url, params, options){
12257     var um = Roo.get(el, true).getUpdateManager();
12258     Roo.apply(um, options);
12259     um.update(url, params, options ? options.callback : null);
12260 };
12261 // alias for backwards compat
12262 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12263 /**
12264  * @class Roo.UpdateManager.BasicRenderer
12265  * Default Content renderer. Updates the elements innerHTML with the responseText.
12266  */
12267 Roo.UpdateManager.BasicRenderer = function(){};
12268
12269 Roo.UpdateManager.BasicRenderer.prototype = {
12270     /**
12271      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12272      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12273      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12274      * @param {Roo.Element} el The element being rendered
12275      * @param {Object} response The YUI Connect response object
12276      * @param {UpdateManager} updateManager The calling update manager
12277      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12278      */
12279      render : function(el, response, updateManager, callback){
12280         el.update(response.responseText, updateManager.loadScripts, callback);
12281     }
12282 };
12283 /*
12284  * Based on:
12285  * Roo JS
12286  * (c)) Alan Knowles
12287  * Licence : LGPL
12288  */
12289
12290
12291 /**
12292  * @class Roo.DomTemplate
12293  * @extends Roo.Template
12294  * An effort at a dom based template engine..
12295  *
12296  * Similar to XTemplate, except it uses dom parsing to create the template..
12297  *
12298  * Supported features:
12299  *
12300  *  Tags:
12301
12302 <pre><code>
12303       {a_variable} - output encoded.
12304       {a_variable.format:("Y-m-d")} - call a method on the variable
12305       {a_variable:raw} - unencoded output
12306       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12307       {a_variable:this.method_on_template(...)} - call a method on the template object.
12308  
12309 </code></pre>
12310  *  The tpl tag:
12311 <pre><code>
12312         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12313         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12314         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12315         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12316   
12317 </code></pre>
12318  *      
12319  */
12320 Roo.DomTemplate = function()
12321 {
12322      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12323      if (this.html) {
12324         this.compile();
12325      }
12326 };
12327
12328
12329 Roo.extend(Roo.DomTemplate, Roo.Template, {
12330     /**
12331      * id counter for sub templates.
12332      */
12333     id : 0,
12334     /**
12335      * flag to indicate if dom parser is inside a pre,
12336      * it will strip whitespace if not.
12337      */
12338     inPre : false,
12339     
12340     /**
12341      * The various sub templates
12342      */
12343     tpls : false,
12344     
12345     
12346     
12347     /**
12348      *
12349      * basic tag replacing syntax
12350      * WORD:WORD()
12351      *
12352      * // you can fake an object call by doing this
12353      *  x.t:(test,tesT) 
12354      * 
12355      */
12356     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12357     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12358     
12359     iterChild : function (node, method) {
12360         
12361         var oldPre = this.inPre;
12362         if (node.tagName == 'PRE') {
12363             this.inPre = true;
12364         }
12365         for( var i = 0; i < node.childNodes.length; i++) {
12366             method.call(this, node.childNodes[i]);
12367         }
12368         this.inPre = oldPre;
12369     },
12370     
12371     
12372     
12373     /**
12374      * compile the template
12375      *
12376      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12377      *
12378      */
12379     compile: function()
12380     {
12381         var s = this.html;
12382         
12383         // covert the html into DOM...
12384         var doc = false;
12385         var div =false;
12386         try {
12387             doc = document.implementation.createHTMLDocument("");
12388             doc.documentElement.innerHTML =   this.html  ;
12389             div = doc.documentElement;
12390         } catch (e) {
12391             // old IE... - nasty -- it causes all sorts of issues.. with
12392             // images getting pulled from server..
12393             div = document.createElement('div');
12394             div.innerHTML = this.html;
12395         }
12396         //doc.documentElement.innerHTML = htmlBody
12397          
12398         
12399         
12400         this.tpls = [];
12401         var _t = this;
12402         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12403         
12404         var tpls = this.tpls;
12405         
12406         // create a top level template from the snippet..
12407         
12408         //Roo.log(div.innerHTML);
12409         
12410         var tpl = {
12411             uid : 'master',
12412             id : this.id++,
12413             attr : false,
12414             value : false,
12415             body : div.innerHTML,
12416             
12417             forCall : false,
12418             execCall : false,
12419             dom : div,
12420             isTop : true
12421             
12422         };
12423         tpls.unshift(tpl);
12424         
12425         
12426         // compile them...
12427         this.tpls = [];
12428         Roo.each(tpls, function(tp){
12429             this.compileTpl(tp);
12430             this.tpls[tp.id] = tp;
12431         }, this);
12432         
12433         this.master = tpls[0];
12434         return this;
12435         
12436         
12437     },
12438     
12439     compileNode : function(node, istop) {
12440         // test for
12441         //Roo.log(node);
12442         
12443         
12444         // skip anything not a tag..
12445         if (node.nodeType != 1) {
12446             if (node.nodeType == 3 && !this.inPre) {
12447                 // reduce white space..
12448                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12449                 
12450             }
12451             return;
12452         }
12453         
12454         var tpl = {
12455             uid : false,
12456             id : false,
12457             attr : false,
12458             value : false,
12459             body : '',
12460             
12461             forCall : false,
12462             execCall : false,
12463             dom : false,
12464             isTop : istop
12465             
12466             
12467         };
12468         
12469         
12470         switch(true) {
12471             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12472             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12473             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12474             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12475             // no default..
12476         }
12477         
12478         
12479         if (!tpl.attr) {
12480             // just itterate children..
12481             this.iterChild(node,this.compileNode);
12482             return;
12483         }
12484         tpl.uid = this.id++;
12485         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12486         node.removeAttribute('roo-'+ tpl.attr);
12487         if (tpl.attr != 'name') {
12488             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12489             node.parentNode.replaceChild(placeholder,  node);
12490         } else {
12491             
12492             var placeholder =  document.createElement('span');
12493             placeholder.className = 'roo-tpl-' + tpl.value;
12494             node.parentNode.replaceChild(placeholder,  node);
12495         }
12496         
12497         // parent now sees '{domtplXXXX}
12498         this.iterChild(node,this.compileNode);
12499         
12500         // we should now have node body...
12501         var div = document.createElement('div');
12502         div.appendChild(node);
12503         tpl.dom = node;
12504         // this has the unfortunate side effect of converting tagged attributes
12505         // eg. href="{...}" into %7C...%7D
12506         // this has been fixed by searching for those combo's although it's a bit hacky..
12507         
12508         
12509         tpl.body = div.innerHTML;
12510         
12511         
12512          
12513         tpl.id = tpl.uid;
12514         switch(tpl.attr) {
12515             case 'for' :
12516                 switch (tpl.value) {
12517                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12518                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12519                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12520                 }
12521                 break;
12522             
12523             case 'exec':
12524                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12525                 break;
12526             
12527             case 'if':     
12528                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12529                 break;
12530             
12531             case 'name':
12532                 tpl.id  = tpl.value; // replace non characters???
12533                 break;
12534             
12535         }
12536         
12537         
12538         this.tpls.push(tpl);
12539         
12540         
12541         
12542     },
12543     
12544     
12545     
12546     
12547     /**
12548      * Compile a segment of the template into a 'sub-template'
12549      *
12550      * 
12551      * 
12552      *
12553      */
12554     compileTpl : function(tpl)
12555     {
12556         var fm = Roo.util.Format;
12557         var useF = this.disableFormats !== true;
12558         
12559         var sep = Roo.isGecko ? "+\n" : ",\n";
12560         
12561         var undef = function(str) {
12562             Roo.debug && Roo.log("Property not found :"  + str);
12563             return '';
12564         };
12565           
12566         //Roo.log(tpl.body);
12567         
12568         
12569         
12570         var fn = function(m, lbrace, name, format, args)
12571         {
12572             //Roo.log("ARGS");
12573             //Roo.log(arguments);
12574             args = args ? args.replace(/\\'/g,"'") : args;
12575             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12576             if (typeof(format) == 'undefined') {
12577                 format =  'htmlEncode'; 
12578             }
12579             if (format == 'raw' ) {
12580                 format = false;
12581             }
12582             
12583             if(name.substr(0, 6) == 'domtpl'){
12584                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12585             }
12586             
12587             // build an array of options to determine if value is undefined..
12588             
12589             // basically get 'xxxx.yyyy' then do
12590             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12591             //    (function () { Roo.log("Property not found"); return ''; })() :
12592             //    ......
12593             
12594             var udef_ar = [];
12595             var lookfor = '';
12596             Roo.each(name.split('.'), function(st) {
12597                 lookfor += (lookfor.length ? '.': '') + st;
12598                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12599             });
12600             
12601             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12602             
12603             
12604             if(format && useF){
12605                 
12606                 args = args ? ',' + args : "";
12607                  
12608                 if(format.substr(0, 5) != "this."){
12609                     format = "fm." + format + '(';
12610                 }else{
12611                     format = 'this.call("'+ format.substr(5) + '", ';
12612                     args = ", values";
12613                 }
12614                 
12615                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12616             }
12617              
12618             if (args && args.length) {
12619                 // called with xxyx.yuu:(test,test)
12620                 // change to ()
12621                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12622             }
12623             // raw.. - :raw modifier..
12624             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12625             
12626         };
12627         var body;
12628         // branched to use + in gecko and [].join() in others
12629         if(Roo.isGecko){
12630             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12631                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12632                     "';};};";
12633         }else{
12634             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12635             body.push(tpl.body.replace(/(\r\n|\n)/g,
12636                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12637             body.push("'].join('');};};");
12638             body = body.join('');
12639         }
12640         
12641         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12642        
12643         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12644         eval(body);
12645         
12646         return this;
12647     },
12648      
12649     /**
12650      * same as applyTemplate, except it's done to one of the subTemplates
12651      * when using named templates, you can do:
12652      *
12653      * var str = pl.applySubTemplate('your-name', values);
12654      *
12655      * 
12656      * @param {Number} id of the template
12657      * @param {Object} values to apply to template
12658      * @param {Object} parent (normaly the instance of this object)
12659      */
12660     applySubTemplate : function(id, values, parent)
12661     {
12662         
12663         
12664         var t = this.tpls[id];
12665         
12666         
12667         try { 
12668             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12669                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12670                 return '';
12671             }
12672         } catch(e) {
12673             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12674             Roo.log(values);
12675           
12676             return '';
12677         }
12678         try { 
12679             
12680             if(t.execCall && t.execCall.call(this, values, parent)){
12681                 return '';
12682             }
12683         } catch(e) {
12684             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12685             Roo.log(values);
12686             return '';
12687         }
12688         
12689         try {
12690             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12691             parent = t.target ? values : parent;
12692             if(t.forCall && vs instanceof Array){
12693                 var buf = [];
12694                 for(var i = 0, len = vs.length; i < len; i++){
12695                     try {
12696                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12697                     } catch (e) {
12698                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12699                         Roo.log(e.body);
12700                         //Roo.log(t.compiled);
12701                         Roo.log(vs[i]);
12702                     }   
12703                 }
12704                 return buf.join('');
12705             }
12706         } catch (e) {
12707             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12708             Roo.log(values);
12709             return '';
12710         }
12711         try {
12712             return t.compiled.call(this, vs, parent);
12713         } catch (e) {
12714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12715             Roo.log(e.body);
12716             //Roo.log(t.compiled);
12717             Roo.log(values);
12718             return '';
12719         }
12720     },
12721
12722    
12723
12724     applyTemplate : function(values){
12725         return this.master.compiled.call(this, values, {});
12726         //var s = this.subs;
12727     },
12728
12729     apply : function(){
12730         return this.applyTemplate.apply(this, arguments);
12731     }
12732
12733  });
12734
12735 Roo.DomTemplate.from = function(el){
12736     el = Roo.getDom(el);
12737     return new Roo.Domtemplate(el.value || el.innerHTML);
12738 };/*
12739  * Based on:
12740  * Ext JS Library 1.1.1
12741  * Copyright(c) 2006-2007, Ext JS, LLC.
12742  *
12743  * Originally Released Under LGPL - original licence link has changed is not relivant.
12744  *
12745  * Fork - LGPL
12746  * <script type="text/javascript">
12747  */
12748
12749 /**
12750  * @class Roo.util.DelayedTask
12751  * Provides a convenient method of performing setTimeout where a new
12752  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12753  * You can use this class to buffer
12754  * the keypress events for a certain number of milliseconds, and perform only if they stop
12755  * for that amount of time.
12756  * @constructor The parameters to this constructor serve as defaults and are not required.
12757  * @param {Function} fn (optional) The default function to timeout
12758  * @param {Object} scope (optional) The default scope of that timeout
12759  * @param {Array} args (optional) The default Array of arguments
12760  */
12761 Roo.util.DelayedTask = function(fn, scope, args){
12762     var id = null, d, t;
12763
12764     var call = function(){
12765         var now = new Date().getTime();
12766         if(now - t >= d){
12767             clearInterval(id);
12768             id = null;
12769             fn.apply(scope, args || []);
12770         }
12771     };
12772     /**
12773      * Cancels any pending timeout and queues a new one
12774      * @param {Number} delay The milliseconds to delay
12775      * @param {Function} newFn (optional) Overrides function passed to constructor
12776      * @param {Object} newScope (optional) Overrides scope passed to constructor
12777      * @param {Array} newArgs (optional) Overrides args passed to constructor
12778      */
12779     this.delay = function(delay, newFn, newScope, newArgs){
12780         if(id && delay != d){
12781             this.cancel();
12782         }
12783         d = delay;
12784         t = new Date().getTime();
12785         fn = newFn || fn;
12786         scope = newScope || scope;
12787         args = newArgs || args;
12788         if(!id){
12789             id = setInterval(call, d);
12790         }
12791     };
12792
12793     /**
12794      * Cancel the last queued timeout
12795      */
12796     this.cancel = function(){
12797         if(id){
12798             clearInterval(id);
12799             id = null;
12800         }
12801     };
12802 };/*
12803  * Based on:
12804  * Ext JS Library 1.1.1
12805  * Copyright(c) 2006-2007, Ext JS, LLC.
12806  *
12807  * Originally Released Under LGPL - original licence link has changed is not relivant.
12808  *
12809  * Fork - LGPL
12810  * <script type="text/javascript">
12811  */
12812  
12813  
12814 Roo.util.TaskRunner = function(interval){
12815     interval = interval || 10;
12816     var tasks = [], removeQueue = [];
12817     var id = 0;
12818     var running = false;
12819
12820     var stopThread = function(){
12821         running = false;
12822         clearInterval(id);
12823         id = 0;
12824     };
12825
12826     var startThread = function(){
12827         if(!running){
12828             running = true;
12829             id = setInterval(runTasks, interval);
12830         }
12831     };
12832
12833     var removeTask = function(task){
12834         removeQueue.push(task);
12835         if(task.onStop){
12836             task.onStop();
12837         }
12838     };
12839
12840     var runTasks = function(){
12841         if(removeQueue.length > 0){
12842             for(var i = 0, len = removeQueue.length; i < len; i++){
12843                 tasks.remove(removeQueue[i]);
12844             }
12845             removeQueue = [];
12846             if(tasks.length < 1){
12847                 stopThread();
12848                 return;
12849             }
12850         }
12851         var now = new Date().getTime();
12852         for(var i = 0, len = tasks.length; i < len; ++i){
12853             var t = tasks[i];
12854             var itime = now - t.taskRunTime;
12855             if(t.interval <= itime){
12856                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12857                 t.taskRunTime = now;
12858                 if(rt === false || t.taskRunCount === t.repeat){
12859                     removeTask(t);
12860                     return;
12861                 }
12862             }
12863             if(t.duration && t.duration <= (now - t.taskStartTime)){
12864                 removeTask(t);
12865             }
12866         }
12867     };
12868
12869     /**
12870      * Queues a new task.
12871      * @param {Object} task
12872      */
12873     this.start = function(task){
12874         tasks.push(task);
12875         task.taskStartTime = new Date().getTime();
12876         task.taskRunTime = 0;
12877         task.taskRunCount = 0;
12878         startThread();
12879         return task;
12880     };
12881
12882     this.stop = function(task){
12883         removeTask(task);
12884         return task;
12885     };
12886
12887     this.stopAll = function(){
12888         stopThread();
12889         for(var i = 0, len = tasks.length; i < len; i++){
12890             if(tasks[i].onStop){
12891                 tasks[i].onStop();
12892             }
12893         }
12894         tasks = [];
12895         removeQueue = [];
12896     };
12897 };
12898
12899 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12900  * Based on:
12901  * Ext JS Library 1.1.1
12902  * Copyright(c) 2006-2007, Ext JS, LLC.
12903  *
12904  * Originally Released Under LGPL - original licence link has changed is not relivant.
12905  *
12906  * Fork - LGPL
12907  * <script type="text/javascript">
12908  */
12909
12910  
12911 /**
12912  * @class Roo.util.MixedCollection
12913  * @extends Roo.util.Observable
12914  * A Collection class that maintains both numeric indexes and keys and exposes events.
12915  * @constructor
12916  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12917  * collection (defaults to false)
12918  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12919  * and return the key value for that item.  This is used when available to look up the key on items that
12920  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12921  * equivalent to providing an implementation for the {@link #getKey} method.
12922  */
12923 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12924     this.items = [];
12925     this.map = {};
12926     this.keys = [];
12927     this.length = 0;
12928     this.addEvents({
12929         /**
12930          * @event clear
12931          * Fires when the collection is cleared.
12932          */
12933         "clear" : true,
12934         /**
12935          * @event add
12936          * Fires when an item is added to the collection.
12937          * @param {Number} index The index at which the item was added.
12938          * @param {Object} o The item added.
12939          * @param {String} key The key associated with the added item.
12940          */
12941         "add" : true,
12942         /**
12943          * @event replace
12944          * Fires when an item is replaced in the collection.
12945          * @param {String} key he key associated with the new added.
12946          * @param {Object} old The item being replaced.
12947          * @param {Object} new The new item.
12948          */
12949         "replace" : true,
12950         /**
12951          * @event remove
12952          * Fires when an item is removed from the collection.
12953          * @param {Object} o The item being removed.
12954          * @param {String} key (optional) The key associated with the removed item.
12955          */
12956         "remove" : true,
12957         "sort" : true
12958     });
12959     this.allowFunctions = allowFunctions === true;
12960     if(keyFn){
12961         this.getKey = keyFn;
12962     }
12963     Roo.util.MixedCollection.superclass.constructor.call(this);
12964 };
12965
12966 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12967     allowFunctions : false,
12968     
12969 /**
12970  * Adds an item to the collection.
12971  * @param {String} key The key to associate with the item
12972  * @param {Object} o The item to add.
12973  * @return {Object} The item added.
12974  */
12975     add : function(key, o){
12976         if(arguments.length == 1){
12977             o = arguments[0];
12978             key = this.getKey(o);
12979         }
12980         if(typeof key == "undefined" || key === null){
12981             this.length++;
12982             this.items.push(o);
12983             this.keys.push(null);
12984         }else{
12985             var old = this.map[key];
12986             if(old){
12987                 return this.replace(key, o);
12988             }
12989             this.length++;
12990             this.items.push(o);
12991             this.map[key] = o;
12992             this.keys.push(key);
12993         }
12994         this.fireEvent("add", this.length-1, o, key);
12995         return o;
12996     },
12997        
12998 /**
12999   * MixedCollection has a generic way to fetch keys if you implement getKey.
13000 <pre><code>
13001 // normal way
13002 var mc = new Roo.util.MixedCollection();
13003 mc.add(someEl.dom.id, someEl);
13004 mc.add(otherEl.dom.id, otherEl);
13005 //and so on
13006
13007 // using getKey
13008 var mc = new Roo.util.MixedCollection();
13009 mc.getKey = function(el){
13010    return el.dom.id;
13011 };
13012 mc.add(someEl);
13013 mc.add(otherEl);
13014
13015 // or via the constructor
13016 var mc = new Roo.util.MixedCollection(false, function(el){
13017    return el.dom.id;
13018 });
13019 mc.add(someEl);
13020 mc.add(otherEl);
13021 </code></pre>
13022  * @param o {Object} The item for which to find the key.
13023  * @return {Object} The key for the passed item.
13024  */
13025     getKey : function(o){
13026          return o.id; 
13027     },
13028    
13029 /**
13030  * Replaces an item in the collection.
13031  * @param {String} key The key associated with the item to replace, or the item to replace.
13032  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13033  * @return {Object}  The new item.
13034  */
13035     replace : function(key, o){
13036         if(arguments.length == 1){
13037             o = arguments[0];
13038             key = this.getKey(o);
13039         }
13040         var old = this.item(key);
13041         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13042              return this.add(key, o);
13043         }
13044         var index = this.indexOfKey(key);
13045         this.items[index] = o;
13046         this.map[key] = o;
13047         this.fireEvent("replace", key, old, o);
13048         return o;
13049     },
13050    
13051 /**
13052  * Adds all elements of an Array or an Object to the collection.
13053  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13054  * an Array of values, each of which are added to the collection.
13055  */
13056     addAll : function(objs){
13057         if(arguments.length > 1 || objs instanceof Array){
13058             var args = arguments.length > 1 ? arguments : objs;
13059             for(var i = 0, len = args.length; i < len; i++){
13060                 this.add(args[i]);
13061             }
13062         }else{
13063             for(var key in objs){
13064                 if(this.allowFunctions || typeof objs[key] != "function"){
13065                     this.add(key, objs[key]);
13066                 }
13067             }
13068         }
13069     },
13070    
13071 /**
13072  * Executes the specified function once for every item in the collection, passing each
13073  * item as the first and only parameter. returning false from the function will stop the iteration.
13074  * @param {Function} fn The function to execute for each item.
13075  * @param {Object} scope (optional) The scope in which to execute the function.
13076  */
13077     each : function(fn, scope){
13078         var items = [].concat(this.items); // each safe for removal
13079         for(var i = 0, len = items.length; i < len; i++){
13080             if(fn.call(scope || items[i], items[i], i, len) === false){
13081                 break;
13082             }
13083         }
13084     },
13085    
13086 /**
13087  * Executes the specified function once for every key in the collection, passing each
13088  * key, and its associated item as the first two parameters.
13089  * @param {Function} fn The function to execute for each item.
13090  * @param {Object} scope (optional) The scope in which to execute the function.
13091  */
13092     eachKey : function(fn, scope){
13093         for(var i = 0, len = this.keys.length; i < len; i++){
13094             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13095         }
13096     },
13097    
13098 /**
13099  * Returns the first item in the collection which elicits a true return value from the
13100  * passed selection function.
13101  * @param {Function} fn The selection function to execute for each item.
13102  * @param {Object} scope (optional) The scope in which to execute the function.
13103  * @return {Object} The first item in the collection which returned true from the selection function.
13104  */
13105     find : function(fn, scope){
13106         for(var i = 0, len = this.items.length; i < len; i++){
13107             if(fn.call(scope || window, this.items[i], this.keys[i])){
13108                 return this.items[i];
13109             }
13110         }
13111         return null;
13112     },
13113    
13114 /**
13115  * Inserts an item at the specified index in the collection.
13116  * @param {Number} index The index to insert the item at.
13117  * @param {String} key The key to associate with the new item, or the item itself.
13118  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13119  * @return {Object} The item inserted.
13120  */
13121     insert : function(index, key, o){
13122         if(arguments.length == 2){
13123             o = arguments[1];
13124             key = this.getKey(o);
13125         }
13126         if(index >= this.length){
13127             return this.add(key, o);
13128         }
13129         this.length++;
13130         this.items.splice(index, 0, o);
13131         if(typeof key != "undefined" && key != null){
13132             this.map[key] = o;
13133         }
13134         this.keys.splice(index, 0, key);
13135         this.fireEvent("add", index, o, key);
13136         return o;
13137     },
13138    
13139 /**
13140  * Removed an item from the collection.
13141  * @param {Object} o The item to remove.
13142  * @return {Object} The item removed.
13143  */
13144     remove : function(o){
13145         return this.removeAt(this.indexOf(o));
13146     },
13147    
13148 /**
13149  * Remove an item from a specified index in the collection.
13150  * @param {Number} index The index within the collection of the item to remove.
13151  */
13152     removeAt : function(index){
13153         if(index < this.length && index >= 0){
13154             this.length--;
13155             var o = this.items[index];
13156             this.items.splice(index, 1);
13157             var key = this.keys[index];
13158             if(typeof key != "undefined"){
13159                 delete this.map[key];
13160             }
13161             this.keys.splice(index, 1);
13162             this.fireEvent("remove", o, key);
13163         }
13164     },
13165    
13166 /**
13167  * Removed an item associated with the passed key fom the collection.
13168  * @param {String} key The key of the item to remove.
13169  */
13170     removeKey : function(key){
13171         return this.removeAt(this.indexOfKey(key));
13172     },
13173    
13174 /**
13175  * Returns the number of items in the collection.
13176  * @return {Number} the number of items in the collection.
13177  */
13178     getCount : function(){
13179         return this.length; 
13180     },
13181    
13182 /**
13183  * Returns index within the collection of the passed Object.
13184  * @param {Object} o The item to find the index of.
13185  * @return {Number} index of the item.
13186  */
13187     indexOf : function(o){
13188         if(!this.items.indexOf){
13189             for(var i = 0, len = this.items.length; i < len; i++){
13190                 if(this.items[i] == o) {
13191                     return i;
13192                 }
13193             }
13194             return -1;
13195         }else{
13196             return this.items.indexOf(o);
13197         }
13198     },
13199    
13200 /**
13201  * Returns index within the collection of the passed key.
13202  * @param {String} key The key to find the index of.
13203  * @return {Number} index of the key.
13204  */
13205     indexOfKey : function(key){
13206         if(!this.keys.indexOf){
13207             for(var i = 0, len = this.keys.length; i < len; i++){
13208                 if(this.keys[i] == key) {
13209                     return i;
13210                 }
13211             }
13212             return -1;
13213         }else{
13214             return this.keys.indexOf(key);
13215         }
13216     },
13217    
13218 /**
13219  * Returns the item associated with the passed key OR index. Key has priority over index.
13220  * @param {String/Number} key The key or index of the item.
13221  * @return {Object} The item associated with the passed key.
13222  */
13223     item : function(key){
13224         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13225         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13226     },
13227     
13228 /**
13229  * Returns the item at the specified index.
13230  * @param {Number} index The index of the item.
13231  * @return {Object}
13232  */
13233     itemAt : function(index){
13234         return this.items[index];
13235     },
13236     
13237 /**
13238  * Returns the item associated with the passed key.
13239  * @param {String/Number} key The key of the item.
13240  * @return {Object} The item associated with the passed key.
13241  */
13242     key : function(key){
13243         return this.map[key];
13244     },
13245    
13246 /**
13247  * Returns true if the collection contains the passed Object as an item.
13248  * @param {Object} o  The Object to look for in the collection.
13249  * @return {Boolean} True if the collection contains the Object as an item.
13250  */
13251     contains : function(o){
13252         return this.indexOf(o) != -1;
13253     },
13254    
13255 /**
13256  * Returns true if the collection contains the passed Object as a key.
13257  * @param {String} key The key to look for in the collection.
13258  * @return {Boolean} True if the collection contains the Object as a key.
13259  */
13260     containsKey : function(key){
13261         return typeof this.map[key] != "undefined";
13262     },
13263    
13264 /**
13265  * Removes all items from the collection.
13266  */
13267     clear : function(){
13268         this.length = 0;
13269         this.items = [];
13270         this.keys = [];
13271         this.map = {};
13272         this.fireEvent("clear");
13273     },
13274    
13275 /**
13276  * Returns the first item in the collection.
13277  * @return {Object} the first item in the collection..
13278  */
13279     first : function(){
13280         return this.items[0]; 
13281     },
13282    
13283 /**
13284  * Returns the last item in the collection.
13285  * @return {Object} the last item in the collection..
13286  */
13287     last : function(){
13288         return this.items[this.length-1];   
13289     },
13290     
13291     _sort : function(property, dir, fn){
13292         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13293         fn = fn || function(a, b){
13294             return a-b;
13295         };
13296         var c = [], k = this.keys, items = this.items;
13297         for(var i = 0, len = items.length; i < len; i++){
13298             c[c.length] = {key: k[i], value: items[i], index: i};
13299         }
13300         c.sort(function(a, b){
13301             var v = fn(a[property], b[property]) * dsc;
13302             if(v == 0){
13303                 v = (a.index < b.index ? -1 : 1);
13304             }
13305             return v;
13306         });
13307         for(var i = 0, len = c.length; i < len; i++){
13308             items[i] = c[i].value;
13309             k[i] = c[i].key;
13310         }
13311         this.fireEvent("sort", this);
13312     },
13313     
13314     /**
13315      * Sorts this collection with the passed comparison function
13316      * @param {String} direction (optional) "ASC" or "DESC"
13317      * @param {Function} fn (optional) comparison function
13318      */
13319     sort : function(dir, fn){
13320         this._sort("value", dir, fn);
13321     },
13322     
13323     /**
13324      * Sorts this collection by keys
13325      * @param {String} direction (optional) "ASC" or "DESC"
13326      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13327      */
13328     keySort : function(dir, fn){
13329         this._sort("key", dir, fn || function(a, b){
13330             return String(a).toUpperCase()-String(b).toUpperCase();
13331         });
13332     },
13333     
13334     /**
13335      * Returns a range of items in this collection
13336      * @param {Number} startIndex (optional) defaults to 0
13337      * @param {Number} endIndex (optional) default to the last item
13338      * @return {Array} An array of items
13339      */
13340     getRange : function(start, end){
13341         var items = this.items;
13342         if(items.length < 1){
13343             return [];
13344         }
13345         start = start || 0;
13346         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13347         var r = [];
13348         if(start <= end){
13349             for(var i = start; i <= end; i++) {
13350                     r[r.length] = items[i];
13351             }
13352         }else{
13353             for(var i = start; i >= end; i--) {
13354                     r[r.length] = items[i];
13355             }
13356         }
13357         return r;
13358     },
13359         
13360     /**
13361      * Filter the <i>objects</i> in this collection by a specific property. 
13362      * Returns a new collection that has been filtered.
13363      * @param {String} property A property on your objects
13364      * @param {String/RegExp} value Either string that the property values 
13365      * should start with or a RegExp to test against the property
13366      * @return {MixedCollection} The new filtered collection
13367      */
13368     filter : function(property, value){
13369         if(!value.exec){ // not a regex
13370             value = String(value);
13371             if(value.length == 0){
13372                 return this.clone();
13373             }
13374             value = new RegExp("^" + Roo.escapeRe(value), "i");
13375         }
13376         return this.filterBy(function(o){
13377             return o && value.test(o[property]);
13378         });
13379         },
13380     
13381     /**
13382      * Filter by a function. * Returns a new collection that has been filtered.
13383      * The passed function will be called with each 
13384      * object in the collection. If the function returns true, the value is included 
13385      * otherwise it is filtered.
13386      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13387      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13388      * @return {MixedCollection} The new filtered collection
13389      */
13390     filterBy : function(fn, scope){
13391         var r = new Roo.util.MixedCollection();
13392         r.getKey = this.getKey;
13393         var k = this.keys, it = this.items;
13394         for(var i = 0, len = it.length; i < len; i++){
13395             if(fn.call(scope||this, it[i], k[i])){
13396                                 r.add(k[i], it[i]);
13397                         }
13398         }
13399         return r;
13400     },
13401     
13402     /**
13403      * Creates a duplicate of this collection
13404      * @return {MixedCollection}
13405      */
13406     clone : function(){
13407         var r = new Roo.util.MixedCollection();
13408         var k = this.keys, it = this.items;
13409         for(var i = 0, len = it.length; i < len; i++){
13410             r.add(k[i], it[i]);
13411         }
13412         r.getKey = this.getKey;
13413         return r;
13414     }
13415 });
13416 /**
13417  * Returns the item associated with the passed key or index.
13418  * @method
13419  * @param {String/Number} key The key or index of the item.
13420  * @return {Object} The item associated with the passed key.
13421  */
13422 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13423  * Based on:
13424  * Ext JS Library 1.1.1
13425  * Copyright(c) 2006-2007, Ext JS, LLC.
13426  *
13427  * Originally Released Under LGPL - original licence link has changed is not relivant.
13428  *
13429  * Fork - LGPL
13430  * <script type="text/javascript">
13431  */
13432 /**
13433  * @class Roo.util.JSON
13434  * Modified version of Douglas Crockford"s json.js that doesn"t
13435  * mess with the Object prototype 
13436  * http://www.json.org/js.html
13437  * @singleton
13438  */
13439 Roo.util.JSON = new (function(){
13440     var useHasOwn = {}.hasOwnProperty ? true : false;
13441     
13442     // crashes Safari in some instances
13443     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13444     
13445     var pad = function(n) {
13446         return n < 10 ? "0" + n : n;
13447     };
13448     
13449     var m = {
13450         "\b": '\\b',
13451         "\t": '\\t',
13452         "\n": '\\n',
13453         "\f": '\\f',
13454         "\r": '\\r',
13455         '"' : '\\"',
13456         "\\": '\\\\'
13457     };
13458
13459     var encodeString = function(s){
13460         if (/["\\\x00-\x1f]/.test(s)) {
13461             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13462                 var c = m[b];
13463                 if(c){
13464                     return c;
13465                 }
13466                 c = b.charCodeAt();
13467                 return "\\u00" +
13468                     Math.floor(c / 16).toString(16) +
13469                     (c % 16).toString(16);
13470             }) + '"';
13471         }
13472         return '"' + s + '"';
13473     };
13474     
13475     var encodeArray = function(o){
13476         var a = ["["], b, i, l = o.length, v;
13477             for (i = 0; i < l; i += 1) {
13478                 v = o[i];
13479                 switch (typeof v) {
13480                     case "undefined":
13481                     case "function":
13482                     case "unknown":
13483                         break;
13484                     default:
13485                         if (b) {
13486                             a.push(',');
13487                         }
13488                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13489                         b = true;
13490                 }
13491             }
13492             a.push("]");
13493             return a.join("");
13494     };
13495     
13496     var encodeDate = function(o){
13497         return '"' + o.getFullYear() + "-" +
13498                 pad(o.getMonth() + 1) + "-" +
13499                 pad(o.getDate()) + "T" +
13500                 pad(o.getHours()) + ":" +
13501                 pad(o.getMinutes()) + ":" +
13502                 pad(o.getSeconds()) + '"';
13503     };
13504     
13505     /**
13506      * Encodes an Object, Array or other value
13507      * @param {Mixed} o The variable to encode
13508      * @return {String} The JSON string
13509      */
13510     this.encode = function(o)
13511     {
13512         // should this be extended to fully wrap stringify..
13513         
13514         if(typeof o == "undefined" || o === null){
13515             return "null";
13516         }else if(o instanceof Array){
13517             return encodeArray(o);
13518         }else if(o instanceof Date){
13519             return encodeDate(o);
13520         }else if(typeof o == "string"){
13521             return encodeString(o);
13522         }else if(typeof o == "number"){
13523             return isFinite(o) ? String(o) : "null";
13524         }else if(typeof o == "boolean"){
13525             return String(o);
13526         }else {
13527             var a = ["{"], b, i, v;
13528             for (i in o) {
13529                 if(!useHasOwn || o.hasOwnProperty(i)) {
13530                     v = o[i];
13531                     switch (typeof v) {
13532                     case "undefined":
13533                     case "function":
13534                     case "unknown":
13535                         break;
13536                     default:
13537                         if(b){
13538                             a.push(',');
13539                         }
13540                         a.push(this.encode(i), ":",
13541                                 v === null ? "null" : this.encode(v));
13542                         b = true;
13543                     }
13544                 }
13545             }
13546             a.push("}");
13547             return a.join("");
13548         }
13549     };
13550     
13551     /**
13552      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13553      * @param {String} json The JSON string
13554      * @return {Object} The resulting object
13555      */
13556     this.decode = function(json){
13557         
13558         return  /** eval:var:json */ eval("(" + json + ')');
13559     };
13560 })();
13561 /** 
13562  * Shorthand for {@link Roo.util.JSON#encode}
13563  * @member Roo encode 
13564  * @method */
13565 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13566 /** 
13567  * Shorthand for {@link Roo.util.JSON#decode}
13568  * @member Roo decode 
13569  * @method */
13570 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13571 /*
13572  * Based on:
13573  * Ext JS Library 1.1.1
13574  * Copyright(c) 2006-2007, Ext JS, LLC.
13575  *
13576  * Originally Released Under LGPL - original licence link has changed is not relivant.
13577  *
13578  * Fork - LGPL
13579  * <script type="text/javascript">
13580  */
13581  
13582 /**
13583  * @class Roo.util.Format
13584  * Reusable data formatting functions
13585  * @singleton
13586  */
13587 Roo.util.Format = function(){
13588     var trimRe = /^\s+|\s+$/g;
13589     return {
13590         /**
13591          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13592          * @param {String} value The string to truncate
13593          * @param {Number} length The maximum length to allow before truncating
13594          * @return {String} The converted text
13595          */
13596         ellipsis : function(value, len){
13597             if(value && value.length > len){
13598                 return value.substr(0, len-3)+"...";
13599             }
13600             return value;
13601         },
13602
13603         /**
13604          * Checks a reference and converts it to empty string if it is undefined
13605          * @param {Mixed} value Reference to check
13606          * @return {Mixed} Empty string if converted, otherwise the original value
13607          */
13608         undef : function(value){
13609             return typeof value != "undefined" ? value : "";
13610         },
13611
13612         /**
13613          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13614          * @param {String} value The string to encode
13615          * @return {String} The encoded text
13616          */
13617         htmlEncode : function(value){
13618             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13619         },
13620
13621         /**
13622          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13623          * @param {String} value The string to decode
13624          * @return {String} The decoded text
13625          */
13626         htmlDecode : function(value){
13627             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13628         },
13629
13630         /**
13631          * Trims any whitespace from either side of a string
13632          * @param {String} value The text to trim
13633          * @return {String} The trimmed text
13634          */
13635         trim : function(value){
13636             return String(value).replace(trimRe, "");
13637         },
13638
13639         /**
13640          * Returns a substring from within an original string
13641          * @param {String} value The original text
13642          * @param {Number} start The start index of the substring
13643          * @param {Number} length The length of the substring
13644          * @return {String} The substring
13645          */
13646         substr : function(value, start, length){
13647             return String(value).substr(start, length);
13648         },
13649
13650         /**
13651          * Converts a string to all lower case letters
13652          * @param {String} value The text to convert
13653          * @return {String} The converted text
13654          */
13655         lowercase : function(value){
13656             return String(value).toLowerCase();
13657         },
13658
13659         /**
13660          * Converts a string to all upper case letters
13661          * @param {String} value The text to convert
13662          * @return {String} The converted text
13663          */
13664         uppercase : function(value){
13665             return String(value).toUpperCase();
13666         },
13667
13668         /**
13669          * Converts the first character only of a string to upper case
13670          * @param {String} value The text to convert
13671          * @return {String} The converted text
13672          */
13673         capitalize : function(value){
13674             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13675         },
13676
13677         // private
13678         call : function(value, fn){
13679             if(arguments.length > 2){
13680                 var args = Array.prototype.slice.call(arguments, 2);
13681                 args.unshift(value);
13682                  
13683                 return /** eval:var:value */  eval(fn).apply(window, args);
13684             }else{
13685                 /** eval:var:value */
13686                 return /** eval:var:value */ eval(fn).call(window, value);
13687             }
13688         },
13689
13690        
13691         /**
13692          * safer version of Math.toFixed..??/
13693          * @param {Number/String} value The numeric value to format
13694          * @param {Number/String} value Decimal places 
13695          * @return {String} The formatted currency string
13696          */
13697         toFixed : function(v, n)
13698         {
13699             // why not use to fixed - precision is buggered???
13700             if (!n) {
13701                 return Math.round(v-0);
13702             }
13703             var fact = Math.pow(10,n+1);
13704             v = (Math.round((v-0)*fact))/fact;
13705             var z = (''+fact).substring(2);
13706             if (v == Math.floor(v)) {
13707                 return Math.floor(v) + '.' + z;
13708             }
13709             
13710             // now just padd decimals..
13711             var ps = String(v).split('.');
13712             var fd = (ps[1] + z);
13713             var r = fd.substring(0,n); 
13714             var rm = fd.substring(n); 
13715             if (rm < 5) {
13716                 return ps[0] + '.' + r;
13717             }
13718             r*=1; // turn it into a number;
13719             r++;
13720             if (String(r).length != n) {
13721                 ps[0]*=1;
13722                 ps[0]++;
13723                 r = String(r).substring(1); // chop the end off.
13724             }
13725             
13726             return ps[0] + '.' + r;
13727              
13728         },
13729         
13730         /**
13731          * Format a number as US currency
13732          * @param {Number/String} value The numeric value to format
13733          * @return {String} The formatted currency string
13734          */
13735         usMoney : function(v){
13736             return '$' + Roo.util.Format.number(v);
13737         },
13738         
13739         /**
13740          * Format a number
13741          * eventually this should probably emulate php's number_format
13742          * @param {Number/String} value The numeric value to format
13743          * @param {Number} decimals number of decimal places
13744          * @return {String} The formatted currency string
13745          */
13746         number : function(v,decimals)
13747         {
13748             // multiply and round.
13749             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13750             var mul = Math.pow(10, decimals);
13751             var zero = String(mul).substring(1);
13752             v = (Math.round((v-0)*mul))/mul;
13753             
13754             // if it's '0' number.. then
13755             
13756             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13757             v = String(v);
13758             var ps = v.split('.');
13759             var whole = ps[0];
13760             
13761             
13762             var r = /(\d+)(\d{3})/;
13763             // add comma's
13764             while (r.test(whole)) {
13765                 whole = whole.replace(r, '$1' + ',' + '$2');
13766             }
13767             
13768             
13769             var sub = ps[1] ?
13770                     // has decimals..
13771                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13772                     // does not have decimals
13773                     (decimals ? ('.' + zero) : '');
13774             
13775             
13776             return whole + sub ;
13777         },
13778         
13779         /**
13780          * Parse a value into a formatted date using the specified format pattern.
13781          * @param {Mixed} value The value to format
13782          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13783          * @return {String} The formatted date string
13784          */
13785         date : function(v, format){
13786             if(!v){
13787                 return "";
13788             }
13789             if(!(v instanceof Date)){
13790                 v = new Date(Date.parse(v));
13791             }
13792             return v.dateFormat(format || Roo.util.Format.defaults.date);
13793         },
13794
13795         /**
13796          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13797          * @param {String} format Any valid date format string
13798          * @return {Function} The date formatting function
13799          */
13800         dateRenderer : function(format){
13801             return function(v){
13802                 return Roo.util.Format.date(v, format);  
13803             };
13804         },
13805
13806         // private
13807         stripTagsRE : /<\/?[^>]+>/gi,
13808         
13809         /**
13810          * Strips all HTML tags
13811          * @param {Mixed} value The text from which to strip tags
13812          * @return {String} The stripped text
13813          */
13814         stripTags : function(v){
13815             return !v ? v : String(v).replace(this.stripTagsRE, "");
13816         }
13817     };
13818 }();
13819 Roo.util.Format.defaults = {
13820     date : 'd/M/Y'
13821 };/*
13822  * Based on:
13823  * Ext JS Library 1.1.1
13824  * Copyright(c) 2006-2007, Ext JS, LLC.
13825  *
13826  * Originally Released Under LGPL - original licence link has changed is not relivant.
13827  *
13828  * Fork - LGPL
13829  * <script type="text/javascript">
13830  */
13831
13832
13833  
13834
13835 /**
13836  * @class Roo.MasterTemplate
13837  * @extends Roo.Template
13838  * Provides a template that can have child templates. The syntax is:
13839 <pre><code>
13840 var t = new Roo.MasterTemplate(
13841         '&lt;select name="{name}"&gt;',
13842                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13843         '&lt;/select&gt;'
13844 );
13845 t.add('options', {value: 'foo', text: 'bar'});
13846 // or you can add multiple child elements in one shot
13847 t.addAll('options', [
13848     {value: 'foo', text: 'bar'},
13849     {value: 'foo2', text: 'bar2'},
13850     {value: 'foo3', text: 'bar3'}
13851 ]);
13852 // then append, applying the master template values
13853 t.append('my-form', {name: 'my-select'});
13854 </code></pre>
13855 * A name attribute for the child template is not required if you have only one child
13856 * template or you want to refer to them by index.
13857  */
13858 Roo.MasterTemplate = function(){
13859     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13860     this.originalHtml = this.html;
13861     var st = {};
13862     var m, re = this.subTemplateRe;
13863     re.lastIndex = 0;
13864     var subIndex = 0;
13865     while(m = re.exec(this.html)){
13866         var name = m[1], content = m[2];
13867         st[subIndex] = {
13868             name: name,
13869             index: subIndex,
13870             buffer: [],
13871             tpl : new Roo.Template(content)
13872         };
13873         if(name){
13874             st[name] = st[subIndex];
13875         }
13876         st[subIndex].tpl.compile();
13877         st[subIndex].tpl.call = this.call.createDelegate(this);
13878         subIndex++;
13879     }
13880     this.subCount = subIndex;
13881     this.subs = st;
13882 };
13883 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13884     /**
13885     * The regular expression used to match sub templates
13886     * @type RegExp
13887     * @property
13888     */
13889     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13890
13891     /**
13892      * Applies the passed values to a child template.
13893      * @param {String/Number} name (optional) The name or index of the child template
13894      * @param {Array/Object} values The values to be applied to the template
13895      * @return {MasterTemplate} this
13896      */
13897      add : function(name, values){
13898         if(arguments.length == 1){
13899             values = arguments[0];
13900             name = 0;
13901         }
13902         var s = this.subs[name];
13903         s.buffer[s.buffer.length] = s.tpl.apply(values);
13904         return this;
13905     },
13906
13907     /**
13908      * Applies all the passed values to a child template.
13909      * @param {String/Number} name (optional) The name or index of the child template
13910      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13911      * @param {Boolean} reset (optional) True to reset the template first
13912      * @return {MasterTemplate} this
13913      */
13914     fill : function(name, values, reset){
13915         var a = arguments;
13916         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13917             values = a[0];
13918             name = 0;
13919             reset = a[1];
13920         }
13921         if(reset){
13922             this.reset();
13923         }
13924         for(var i = 0, len = values.length; i < len; i++){
13925             this.add(name, values[i]);
13926         }
13927         return this;
13928     },
13929
13930     /**
13931      * Resets the template for reuse
13932      * @return {MasterTemplate} this
13933      */
13934      reset : function(){
13935         var s = this.subs;
13936         for(var i = 0; i < this.subCount; i++){
13937             s[i].buffer = [];
13938         }
13939         return this;
13940     },
13941
13942     applyTemplate : function(values){
13943         var s = this.subs;
13944         var replaceIndex = -1;
13945         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13946             return s[++replaceIndex].buffer.join("");
13947         });
13948         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13949     },
13950
13951     apply : function(){
13952         return this.applyTemplate.apply(this, arguments);
13953     },
13954
13955     compile : function(){return this;}
13956 });
13957
13958 /**
13959  * Alias for fill().
13960  * @method
13961  */
13962 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13963  /**
13964  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13965  * var tpl = Roo.MasterTemplate.from('element-id');
13966  * @param {String/HTMLElement} el
13967  * @param {Object} config
13968  * @static
13969  */
13970 Roo.MasterTemplate.from = function(el, config){
13971     el = Roo.getDom(el);
13972     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13973 };/*
13974  * Based on:
13975  * Ext JS Library 1.1.1
13976  * Copyright(c) 2006-2007, Ext JS, LLC.
13977  *
13978  * Originally Released Under LGPL - original licence link has changed is not relivant.
13979  *
13980  * Fork - LGPL
13981  * <script type="text/javascript">
13982  */
13983
13984  
13985 /**
13986  * @class Roo.util.CSS
13987  * Utility class for manipulating CSS rules
13988  * @singleton
13989  */
13990 Roo.util.CSS = function(){
13991         var rules = null;
13992         var doc = document;
13993
13994     var camelRe = /(-[a-z])/gi;
13995     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13996
13997    return {
13998    /**
13999     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14000     * tag and appended to the HEAD of the document.
14001     * @param {String|Object} cssText The text containing the css rules
14002     * @param {String} id An id to add to the stylesheet for later removal
14003     * @return {StyleSheet}
14004     */
14005     createStyleSheet : function(cssText, id){
14006         var ss;
14007         var head = doc.getElementsByTagName("head")[0];
14008         var nrules = doc.createElement("style");
14009         nrules.setAttribute("type", "text/css");
14010         if(id){
14011             nrules.setAttribute("id", id);
14012         }
14013         if (typeof(cssText) != 'string') {
14014             // support object maps..
14015             // not sure if this a good idea.. 
14016             // perhaps it should be merged with the general css handling
14017             // and handle js style props.
14018             var cssTextNew = [];
14019             for(var n in cssText) {
14020                 var citems = [];
14021                 for(var k in cssText[n]) {
14022                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14023                 }
14024                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14025                 
14026             }
14027             cssText = cssTextNew.join("\n");
14028             
14029         }
14030        
14031        
14032        if(Roo.isIE){
14033            head.appendChild(nrules);
14034            ss = nrules.styleSheet;
14035            ss.cssText = cssText;
14036        }else{
14037            try{
14038                 nrules.appendChild(doc.createTextNode(cssText));
14039            }catch(e){
14040                nrules.cssText = cssText; 
14041            }
14042            head.appendChild(nrules);
14043            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14044        }
14045        this.cacheStyleSheet(ss);
14046        return ss;
14047    },
14048
14049    /**
14050     * Removes a style or link tag by id
14051     * @param {String} id The id of the tag
14052     */
14053    removeStyleSheet : function(id){
14054        var existing = doc.getElementById(id);
14055        if(existing){
14056            existing.parentNode.removeChild(existing);
14057        }
14058    },
14059
14060    /**
14061     * Dynamically swaps an existing stylesheet reference for a new one
14062     * @param {String} id The id of an existing link tag to remove
14063     * @param {String} url The href of the new stylesheet to include
14064     */
14065    swapStyleSheet : function(id, url){
14066        this.removeStyleSheet(id);
14067        var ss = doc.createElement("link");
14068        ss.setAttribute("rel", "stylesheet");
14069        ss.setAttribute("type", "text/css");
14070        ss.setAttribute("id", id);
14071        ss.setAttribute("href", url);
14072        doc.getElementsByTagName("head")[0].appendChild(ss);
14073    },
14074    
14075    /**
14076     * Refresh the rule cache if you have dynamically added stylesheets
14077     * @return {Object} An object (hash) of rules indexed by selector
14078     */
14079    refreshCache : function(){
14080        return this.getRules(true);
14081    },
14082
14083    // private
14084    cacheStyleSheet : function(stylesheet){
14085        if(!rules){
14086            rules = {};
14087        }
14088        try{// try catch for cross domain access issue
14089            var ssRules = stylesheet.cssRules || stylesheet.rules;
14090            for(var j = ssRules.length-1; j >= 0; --j){
14091                rules[ssRules[j].selectorText] = ssRules[j];
14092            }
14093        }catch(e){}
14094    },
14095    
14096    /**
14097     * Gets all css rules for the document
14098     * @param {Boolean} refreshCache true to refresh the internal cache
14099     * @return {Object} An object (hash) of rules indexed by selector
14100     */
14101    getRules : function(refreshCache){
14102                 if(rules == null || refreshCache){
14103                         rules = {};
14104                         var ds = doc.styleSheets;
14105                         for(var i =0, len = ds.length; i < len; i++){
14106                             try{
14107                         this.cacheStyleSheet(ds[i]);
14108                     }catch(e){} 
14109                 }
14110                 }
14111                 return rules;
14112         },
14113         
14114         /**
14115     * Gets an an individual CSS rule by selector(s)
14116     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14117     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14118     * @return {CSSRule} The CSS rule or null if one is not found
14119     */
14120    getRule : function(selector, refreshCache){
14121                 var rs = this.getRules(refreshCache);
14122                 if(!(selector instanceof Array)){
14123                     return rs[selector];
14124                 }
14125                 for(var i = 0; i < selector.length; i++){
14126                         if(rs[selector[i]]){
14127                                 return rs[selector[i]];
14128                         }
14129                 }
14130                 return null;
14131         },
14132         
14133         
14134         /**
14135     * Updates a rule property
14136     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14137     * @param {String} property The css property
14138     * @param {String} value The new value for the property
14139     * @return {Boolean} true If a rule was found and updated
14140     */
14141    updateRule : function(selector, property, value){
14142                 if(!(selector instanceof Array)){
14143                         var rule = this.getRule(selector);
14144                         if(rule){
14145                                 rule.style[property.replace(camelRe, camelFn)] = value;
14146                                 return true;
14147                         }
14148                 }else{
14149                         for(var i = 0; i < selector.length; i++){
14150                                 if(this.updateRule(selector[i], property, value)){
14151                                         return true;
14152                                 }
14153                         }
14154                 }
14155                 return false;
14156         }
14157    };   
14158 }();/*
14159  * Based on:
14160  * Ext JS Library 1.1.1
14161  * Copyright(c) 2006-2007, Ext JS, LLC.
14162  *
14163  * Originally Released Under LGPL - original licence link has changed is not relivant.
14164  *
14165  * Fork - LGPL
14166  * <script type="text/javascript">
14167  */
14168
14169  
14170
14171 /**
14172  * @class Roo.util.ClickRepeater
14173  * @extends Roo.util.Observable
14174  * 
14175  * A wrapper class which can be applied to any element. Fires a "click" event while the
14176  * mouse is pressed. The interval between firings may be specified in the config but
14177  * defaults to 10 milliseconds.
14178  * 
14179  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14180  * 
14181  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14182  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14183  * Similar to an autorepeat key delay.
14184  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14185  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14186  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14187  *           "interval" and "delay" are ignored. "immediate" is honored.
14188  * @cfg {Boolean} preventDefault True to prevent the default click event
14189  * @cfg {Boolean} stopDefault True to stop the default click event
14190  * 
14191  * @history
14192  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14193  *     2007-02-02 jvs Renamed to ClickRepeater
14194  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14195  *
14196  *  @constructor
14197  * @param {String/HTMLElement/Element} el The element to listen on
14198  * @param {Object} config
14199  **/
14200 Roo.util.ClickRepeater = function(el, config)
14201 {
14202     this.el = Roo.get(el);
14203     this.el.unselectable();
14204
14205     Roo.apply(this, config);
14206
14207     this.addEvents({
14208     /**
14209      * @event mousedown
14210      * Fires when the mouse button is depressed.
14211      * @param {Roo.util.ClickRepeater} this
14212      */
14213         "mousedown" : true,
14214     /**
14215      * @event click
14216      * Fires on a specified interval during the time the element is pressed.
14217      * @param {Roo.util.ClickRepeater} this
14218      */
14219         "click" : true,
14220     /**
14221      * @event mouseup
14222      * Fires when the mouse key is released.
14223      * @param {Roo.util.ClickRepeater} this
14224      */
14225         "mouseup" : true
14226     });
14227
14228     this.el.on("mousedown", this.handleMouseDown, this);
14229     if(this.preventDefault || this.stopDefault){
14230         this.el.on("click", function(e){
14231             if(this.preventDefault){
14232                 e.preventDefault();
14233             }
14234             if(this.stopDefault){
14235                 e.stopEvent();
14236             }
14237         }, this);
14238     }
14239
14240     // allow inline handler
14241     if(this.handler){
14242         this.on("click", this.handler,  this.scope || this);
14243     }
14244
14245     Roo.util.ClickRepeater.superclass.constructor.call(this);
14246 };
14247
14248 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14249     interval : 20,
14250     delay: 250,
14251     preventDefault : true,
14252     stopDefault : false,
14253     timer : 0,
14254
14255     // private
14256     handleMouseDown : function(){
14257         clearTimeout(this.timer);
14258         this.el.blur();
14259         if(this.pressClass){
14260             this.el.addClass(this.pressClass);
14261         }
14262         this.mousedownTime = new Date();
14263
14264         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14265         this.el.on("mouseout", this.handleMouseOut, this);
14266
14267         this.fireEvent("mousedown", this);
14268         this.fireEvent("click", this);
14269         
14270         this.timer = this.click.defer(this.delay || this.interval, this);
14271     },
14272
14273     // private
14274     click : function(){
14275         this.fireEvent("click", this);
14276         this.timer = this.click.defer(this.getInterval(), this);
14277     },
14278
14279     // private
14280     getInterval: function(){
14281         if(!this.accelerate){
14282             return this.interval;
14283         }
14284         var pressTime = this.mousedownTime.getElapsed();
14285         if(pressTime < 500){
14286             return 400;
14287         }else if(pressTime < 1700){
14288             return 320;
14289         }else if(pressTime < 2600){
14290             return 250;
14291         }else if(pressTime < 3500){
14292             return 180;
14293         }else if(pressTime < 4400){
14294             return 140;
14295         }else if(pressTime < 5300){
14296             return 80;
14297         }else if(pressTime < 6200){
14298             return 50;
14299         }else{
14300             return 10;
14301         }
14302     },
14303
14304     // private
14305     handleMouseOut : function(){
14306         clearTimeout(this.timer);
14307         if(this.pressClass){
14308             this.el.removeClass(this.pressClass);
14309         }
14310         this.el.on("mouseover", this.handleMouseReturn, this);
14311     },
14312
14313     // private
14314     handleMouseReturn : function(){
14315         this.el.un("mouseover", this.handleMouseReturn);
14316         if(this.pressClass){
14317             this.el.addClass(this.pressClass);
14318         }
14319         this.click();
14320     },
14321
14322     // private
14323     handleMouseUp : function(){
14324         clearTimeout(this.timer);
14325         this.el.un("mouseover", this.handleMouseReturn);
14326         this.el.un("mouseout", this.handleMouseOut);
14327         Roo.get(document).un("mouseup", this.handleMouseUp);
14328         this.el.removeClass(this.pressClass);
14329         this.fireEvent("mouseup", this);
14330     }
14331 });/*
14332  * Based on:
14333  * Ext JS Library 1.1.1
14334  * Copyright(c) 2006-2007, Ext JS, LLC.
14335  *
14336  * Originally Released Under LGPL - original licence link has changed is not relivant.
14337  *
14338  * Fork - LGPL
14339  * <script type="text/javascript">
14340  */
14341
14342  
14343 /**
14344  * @class Roo.KeyNav
14345  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14346  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14347  * way to implement custom navigation schemes for any UI component.</p>
14348  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14349  * pageUp, pageDown, del, home, end.  Usage:</p>
14350  <pre><code>
14351 var nav = new Roo.KeyNav("my-element", {
14352     "left" : function(e){
14353         this.moveLeft(e.ctrlKey);
14354     },
14355     "right" : function(e){
14356         this.moveRight(e.ctrlKey);
14357     },
14358     "enter" : function(e){
14359         this.save();
14360     },
14361     scope : this
14362 });
14363 </code></pre>
14364  * @constructor
14365  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14366  * @param {Object} config The config
14367  */
14368 Roo.KeyNav = function(el, config){
14369     this.el = Roo.get(el);
14370     Roo.apply(this, config);
14371     if(!this.disabled){
14372         this.disabled = true;
14373         this.enable();
14374     }
14375 };
14376
14377 Roo.KeyNav.prototype = {
14378     /**
14379      * @cfg {Boolean} disabled
14380      * True to disable this KeyNav instance (defaults to false)
14381      */
14382     disabled : false,
14383     /**
14384      * @cfg {String} defaultEventAction
14385      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14386      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14387      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14388      */
14389     defaultEventAction: "stopEvent",
14390     /**
14391      * @cfg {Boolean} forceKeyDown
14392      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14393      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14394      * handle keydown instead of keypress.
14395      */
14396     forceKeyDown : false,
14397
14398     // private
14399     prepareEvent : function(e){
14400         var k = e.getKey();
14401         var h = this.keyToHandler[k];
14402         //if(h && this[h]){
14403         //    e.stopPropagation();
14404         //}
14405         if(Roo.isSafari && h && k >= 37 && k <= 40){
14406             e.stopEvent();
14407         }
14408     },
14409
14410     // private
14411     relay : function(e){
14412         var k = e.getKey();
14413         var h = this.keyToHandler[k];
14414         if(h && this[h]){
14415             if(this.doRelay(e, this[h], h) !== true){
14416                 e[this.defaultEventAction]();
14417             }
14418         }
14419     },
14420
14421     // private
14422     doRelay : function(e, h, hname){
14423         return h.call(this.scope || this, e);
14424     },
14425
14426     // possible handlers
14427     enter : false,
14428     left : false,
14429     right : false,
14430     up : false,
14431     down : false,
14432     tab : false,
14433     esc : false,
14434     pageUp : false,
14435     pageDown : false,
14436     del : false,
14437     home : false,
14438     end : false,
14439
14440     // quick lookup hash
14441     keyToHandler : {
14442         37 : "left",
14443         39 : "right",
14444         38 : "up",
14445         40 : "down",
14446         33 : "pageUp",
14447         34 : "pageDown",
14448         46 : "del",
14449         36 : "home",
14450         35 : "end",
14451         13 : "enter",
14452         27 : "esc",
14453         9  : "tab"
14454     },
14455
14456         /**
14457          * Enable this KeyNav
14458          */
14459         enable: function(){
14460                 if(this.disabled){
14461             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14462             // the EventObject will normalize Safari automatically
14463             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14464                 this.el.on("keydown", this.relay,  this);
14465             }else{
14466                 this.el.on("keydown", this.prepareEvent,  this);
14467                 this.el.on("keypress", this.relay,  this);
14468             }
14469                     this.disabled = false;
14470                 }
14471         },
14472
14473         /**
14474          * Disable this KeyNav
14475          */
14476         disable: function(){
14477                 if(!this.disabled){
14478                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14479                 this.el.un("keydown", this.relay);
14480             }else{
14481                 this.el.un("keydown", this.prepareEvent);
14482                 this.el.un("keypress", this.relay);
14483             }
14484                     this.disabled = true;
14485                 }
14486         }
14487 };/*
14488  * Based on:
14489  * Ext JS Library 1.1.1
14490  * Copyright(c) 2006-2007, Ext JS, LLC.
14491  *
14492  * Originally Released Under LGPL - original licence link has changed is not relivant.
14493  *
14494  * Fork - LGPL
14495  * <script type="text/javascript">
14496  */
14497
14498  
14499 /**
14500  * @class Roo.KeyMap
14501  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14502  * The constructor accepts the same config object as defined by {@link #addBinding}.
14503  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14504  * combination it will call the function with this signature (if the match is a multi-key
14505  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14506  * A KeyMap can also handle a string representation of keys.<br />
14507  * Usage:
14508  <pre><code>
14509 // map one key by key code
14510 var map = new Roo.KeyMap("my-element", {
14511     key: 13, // or Roo.EventObject.ENTER
14512     fn: myHandler,
14513     scope: myObject
14514 });
14515
14516 // map multiple keys to one action by string
14517 var map = new Roo.KeyMap("my-element", {
14518     key: "a\r\n\t",
14519     fn: myHandler,
14520     scope: myObject
14521 });
14522
14523 // map multiple keys to multiple actions by strings and array of codes
14524 var map = new Roo.KeyMap("my-element", [
14525     {
14526         key: [10,13],
14527         fn: function(){ alert("Return was pressed"); }
14528     }, {
14529         key: "abc",
14530         fn: function(){ alert('a, b or c was pressed'); }
14531     }, {
14532         key: "\t",
14533         ctrl:true,
14534         shift:true,
14535         fn: function(){ alert('Control + shift + tab was pressed.'); }
14536     }
14537 ]);
14538 </code></pre>
14539  * <b>Note: A KeyMap starts enabled</b>
14540  * @constructor
14541  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14542  * @param {Object} config The config (see {@link #addBinding})
14543  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14544  */
14545 Roo.KeyMap = function(el, config, eventName){
14546     this.el  = Roo.get(el);
14547     this.eventName = eventName || "keydown";
14548     this.bindings = [];
14549     if(config){
14550         this.addBinding(config);
14551     }
14552     this.enable();
14553 };
14554
14555 Roo.KeyMap.prototype = {
14556     /**
14557      * True to stop the event from bubbling and prevent the default browser action if the
14558      * key was handled by the KeyMap (defaults to false)
14559      * @type Boolean
14560      */
14561     stopEvent : false,
14562
14563     /**
14564      * Add a new binding to this KeyMap. The following config object properties are supported:
14565      * <pre>
14566 Property    Type             Description
14567 ----------  ---------------  ----------------------------------------------------------------------
14568 key         String/Array     A single keycode or an array of keycodes to handle
14569 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14570 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14571 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14572 fn          Function         The function to call when KeyMap finds the expected key combination
14573 scope       Object           The scope of the callback function
14574 </pre>
14575      *
14576      * Usage:
14577      * <pre><code>
14578 // Create a KeyMap
14579 var map = new Roo.KeyMap(document, {
14580     key: Roo.EventObject.ENTER,
14581     fn: handleKey,
14582     scope: this
14583 });
14584
14585 //Add a new binding to the existing KeyMap later
14586 map.addBinding({
14587     key: 'abc',
14588     shift: true,
14589     fn: handleKey,
14590     scope: this
14591 });
14592 </code></pre>
14593      * @param {Object/Array} config A single KeyMap config or an array of configs
14594      */
14595         addBinding : function(config){
14596         if(config instanceof Array){
14597             for(var i = 0, len = config.length; i < len; i++){
14598                 this.addBinding(config[i]);
14599             }
14600             return;
14601         }
14602         var keyCode = config.key,
14603             shift = config.shift, 
14604             ctrl = config.ctrl, 
14605             alt = config.alt,
14606             fn = config.fn,
14607             scope = config.scope;
14608         if(typeof keyCode == "string"){
14609             var ks = [];
14610             var keyString = keyCode.toUpperCase();
14611             for(var j = 0, len = keyString.length; j < len; j++){
14612                 ks.push(keyString.charCodeAt(j));
14613             }
14614             keyCode = ks;
14615         }
14616         var keyArray = keyCode instanceof Array;
14617         var handler = function(e){
14618             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14619                 var k = e.getKey();
14620                 if(keyArray){
14621                     for(var i = 0, len = keyCode.length; i < len; i++){
14622                         if(keyCode[i] == k){
14623                           if(this.stopEvent){
14624                               e.stopEvent();
14625                           }
14626                           fn.call(scope || window, k, e);
14627                           return;
14628                         }
14629                     }
14630                 }else{
14631                     if(k == keyCode){
14632                         if(this.stopEvent){
14633                            e.stopEvent();
14634                         }
14635                         fn.call(scope || window, k, e);
14636                     }
14637                 }
14638             }
14639         };
14640         this.bindings.push(handler);  
14641         },
14642
14643     /**
14644      * Shorthand for adding a single key listener
14645      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14646      * following options:
14647      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      */
14651     on : function(key, fn, scope){
14652         var keyCode, shift, ctrl, alt;
14653         if(typeof key == "object" && !(key instanceof Array)){
14654             keyCode = key.key;
14655             shift = key.shift;
14656             ctrl = key.ctrl;
14657             alt = key.alt;
14658         }else{
14659             keyCode = key;
14660         }
14661         this.addBinding({
14662             key: keyCode,
14663             shift: shift,
14664             ctrl: ctrl,
14665             alt: alt,
14666             fn: fn,
14667             scope: scope
14668         })
14669     },
14670
14671     // private
14672     handleKeyDown : function(e){
14673             if(this.enabled){ //just in case
14674             var b = this.bindings;
14675             for(var i = 0, len = b.length; i < len; i++){
14676                 b[i].call(this, e);
14677             }
14678             }
14679         },
14680         
14681         /**
14682          * Returns true if this KeyMap is enabled
14683          * @return {Boolean} 
14684          */
14685         isEnabled : function(){
14686             return this.enabled;  
14687         },
14688         
14689         /**
14690          * Enables this KeyMap
14691          */
14692         enable: function(){
14693                 if(!this.enabled){
14694                     this.el.on(this.eventName, this.handleKeyDown, this);
14695                     this.enabled = true;
14696                 }
14697         },
14698
14699         /**
14700          * Disable this KeyMap
14701          */
14702         disable: function(){
14703                 if(this.enabled){
14704                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14705                     this.enabled = false;
14706                 }
14707         }
14708 };/*
14709  * Based on:
14710  * Ext JS Library 1.1.1
14711  * Copyright(c) 2006-2007, Ext JS, LLC.
14712  *
14713  * Originally Released Under LGPL - original licence link has changed is not relivant.
14714  *
14715  * Fork - LGPL
14716  * <script type="text/javascript">
14717  */
14718
14719  
14720 /**
14721  * @class Roo.util.TextMetrics
14722  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14723  * wide, in pixels, a given block of text will be.
14724  * @singleton
14725  */
14726 Roo.util.TextMetrics = function(){
14727     var shared;
14728     return {
14729         /**
14730          * Measures the size of the specified text
14731          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14732          * that can affect the size of the rendered text
14733          * @param {String} text The text to measure
14734          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14735          * in order to accurately measure the text height
14736          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14737          */
14738         measure : function(el, text, fixedWidth){
14739             if(!shared){
14740                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14741             }
14742             shared.bind(el);
14743             shared.setFixedWidth(fixedWidth || 'auto');
14744             return shared.getSize(text);
14745         },
14746
14747         /**
14748          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14749          * the overhead of multiple calls to initialize the style properties on each measurement.
14750          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14751          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14752          * in order to accurately measure the text height
14753          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14754          */
14755         createInstance : function(el, fixedWidth){
14756             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14757         }
14758     };
14759 }();
14760
14761  
14762
14763 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14764     var ml = new Roo.Element(document.createElement('div'));
14765     document.body.appendChild(ml.dom);
14766     ml.position('absolute');
14767     ml.setLeftTop(-1000, -1000);
14768     ml.hide();
14769
14770     if(fixedWidth){
14771         ml.setWidth(fixedWidth);
14772     }
14773      
14774     var instance = {
14775         /**
14776          * Returns the size of the specified text based on the internal element's style and width properties
14777          * @memberOf Roo.util.TextMetrics.Instance#
14778          * @param {String} text The text to measure
14779          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14780          */
14781         getSize : function(text){
14782             ml.update(text);
14783             var s = ml.getSize();
14784             ml.update('');
14785             return s;
14786         },
14787
14788         /**
14789          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14790          * that can affect the size of the rendered text
14791          * @memberOf Roo.util.TextMetrics.Instance#
14792          * @param {String/HTMLElement} el The element, dom node or id
14793          */
14794         bind : function(el){
14795             ml.setStyle(
14796                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14797             );
14798         },
14799
14800         /**
14801          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14802          * to set a fixed width in order to accurately measure the text height.
14803          * @memberOf Roo.util.TextMetrics.Instance#
14804          * @param {Number} width The width to set on the element
14805          */
14806         setFixedWidth : function(width){
14807             ml.setWidth(width);
14808         },
14809
14810         /**
14811          * Returns the measured width of the specified text
14812          * @memberOf Roo.util.TextMetrics.Instance#
14813          * @param {String} text The text to measure
14814          * @return {Number} width The width in pixels
14815          */
14816         getWidth : function(text){
14817             ml.dom.style.width = 'auto';
14818             return this.getSize(text).width;
14819         },
14820
14821         /**
14822          * Returns the measured height of the specified text.  For multiline text, be sure to call
14823          * {@link #setFixedWidth} if necessary.
14824          * @memberOf Roo.util.TextMetrics.Instance#
14825          * @param {String} text The text to measure
14826          * @return {Number} height The height in pixels
14827          */
14828         getHeight : function(text){
14829             return this.getSize(text).height;
14830         }
14831     };
14832
14833     instance.bind(bindTo);
14834
14835     return instance;
14836 };
14837
14838 // backwards compat
14839 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14840  * Based on:
14841  * Ext JS Library 1.1.1
14842  * Copyright(c) 2006-2007, Ext JS, LLC.
14843  *
14844  * Originally Released Under LGPL - original licence link has changed is not relivant.
14845  *
14846  * Fork - LGPL
14847  * <script type="text/javascript">
14848  */
14849
14850 /**
14851  * @class Roo.state.Provider
14852  * Abstract base class for state provider implementations. This class provides methods
14853  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14854  * Provider interface.
14855  */
14856 Roo.state.Provider = function(){
14857     /**
14858      * @event statechange
14859      * Fires when a state change occurs.
14860      * @param {Provider} this This state provider
14861      * @param {String} key The state key which was changed
14862      * @param {String} value The encoded value for the state
14863      */
14864     this.addEvents({
14865         "statechange": true
14866     });
14867     this.state = {};
14868     Roo.state.Provider.superclass.constructor.call(this);
14869 };
14870 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14871     /**
14872      * Returns the current value for a key
14873      * @param {String} name The key name
14874      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14875      * @return {Mixed} The state data
14876      */
14877     get : function(name, defaultValue){
14878         return typeof this.state[name] == "undefined" ?
14879             defaultValue : this.state[name];
14880     },
14881     
14882     /**
14883      * Clears a value from the state
14884      * @param {String} name The key name
14885      */
14886     clear : function(name){
14887         delete this.state[name];
14888         this.fireEvent("statechange", this, name, null);
14889     },
14890     
14891     /**
14892      * Sets the value for a key
14893      * @param {String} name The key name
14894      * @param {Mixed} value The value to set
14895      */
14896     set : function(name, value){
14897         this.state[name] = value;
14898         this.fireEvent("statechange", this, name, value);
14899     },
14900     
14901     /**
14902      * Decodes a string previously encoded with {@link #encodeValue}.
14903      * @param {String} value The value to decode
14904      * @return {Mixed} The decoded value
14905      */
14906     decodeValue : function(cookie){
14907         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14908         var matches = re.exec(unescape(cookie));
14909         if(!matches || !matches[1]) {
14910             return; // non state cookie
14911         }
14912         var type = matches[1];
14913         var v = matches[2];
14914         switch(type){
14915             case "n":
14916                 return parseFloat(v);
14917             case "d":
14918                 return new Date(Date.parse(v));
14919             case "b":
14920                 return (v == "1");
14921             case "a":
14922                 var all = [];
14923                 var values = v.split("^");
14924                 for(var i = 0, len = values.length; i < len; i++){
14925                     all.push(this.decodeValue(values[i]));
14926                 }
14927                 return all;
14928            case "o":
14929                 var all = {};
14930                 var values = v.split("^");
14931                 for(var i = 0, len = values.length; i < len; i++){
14932                     var kv = values[i].split("=");
14933                     all[kv[0]] = this.decodeValue(kv[1]);
14934                 }
14935                 return all;
14936            default:
14937                 return v;
14938         }
14939     },
14940     
14941     /**
14942      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14943      * @param {Mixed} value The value to encode
14944      * @return {String} The encoded value
14945      */
14946     encodeValue : function(v){
14947         var enc;
14948         if(typeof v == "number"){
14949             enc = "n:" + v;
14950         }else if(typeof v == "boolean"){
14951             enc = "b:" + (v ? "1" : "0");
14952         }else if(v instanceof Date){
14953             enc = "d:" + v.toGMTString();
14954         }else if(v instanceof Array){
14955             var flat = "";
14956             for(var i = 0, len = v.length; i < len; i++){
14957                 flat += this.encodeValue(v[i]);
14958                 if(i != len-1) {
14959                     flat += "^";
14960                 }
14961             }
14962             enc = "a:" + flat;
14963         }else if(typeof v == "object"){
14964             var flat = "";
14965             for(var key in v){
14966                 if(typeof v[key] != "function"){
14967                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14968                 }
14969             }
14970             enc = "o:" + flat.substring(0, flat.length-1);
14971         }else{
14972             enc = "s:" + v;
14973         }
14974         return escape(enc);        
14975     }
14976 });
14977
14978 /*
14979  * Based on:
14980  * Ext JS Library 1.1.1
14981  * Copyright(c) 2006-2007, Ext JS, LLC.
14982  *
14983  * Originally Released Under LGPL - original licence link has changed is not relivant.
14984  *
14985  * Fork - LGPL
14986  * <script type="text/javascript">
14987  */
14988 /**
14989  * @class Roo.state.Manager
14990  * This is the global state manager. By default all components that are "state aware" check this class
14991  * for state information if you don't pass them a custom state provider. In order for this class
14992  * to be useful, it must be initialized with a provider when your application initializes.
14993  <pre><code>
14994 // in your initialization function
14995 init : function(){
14996    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14997    ...
14998    // supposed you have a {@link Roo.BorderLayout}
14999    var layout = new Roo.BorderLayout(...);
15000    layout.restoreState();
15001    // or a {Roo.BasicDialog}
15002    var dialog = new Roo.BasicDialog(...);
15003    dialog.restoreState();
15004  </code></pre>
15005  * @singleton
15006  */
15007 Roo.state.Manager = function(){
15008     var provider = new Roo.state.Provider();
15009     
15010     return {
15011         /**
15012          * Configures the default state provider for your application
15013          * @param {Provider} stateProvider The state provider to set
15014          */
15015         setProvider : function(stateProvider){
15016             provider = stateProvider;
15017         },
15018         
15019         /**
15020          * Returns the current value for a key
15021          * @param {String} name The key name
15022          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15023          * @return {Mixed} The state data
15024          */
15025         get : function(key, defaultValue){
15026             return provider.get(key, defaultValue);
15027         },
15028         
15029         /**
15030          * Sets the value for a key
15031          * @param {String} name The key name
15032          * @param {Mixed} value The state data
15033          */
15034          set : function(key, value){
15035             provider.set(key, value);
15036         },
15037         
15038         /**
15039          * Clears a value from the state
15040          * @param {String} name The key name
15041          */
15042         clear : function(key){
15043             provider.clear(key);
15044         },
15045         
15046         /**
15047          * Gets the currently configured state provider
15048          * @return {Provider} The state provider
15049          */
15050         getProvider : function(){
15051             return provider;
15052         }
15053     };
15054 }();
15055 /*
15056  * Based on:
15057  * Ext JS Library 1.1.1
15058  * Copyright(c) 2006-2007, Ext JS, LLC.
15059  *
15060  * Originally Released Under LGPL - original licence link has changed is not relivant.
15061  *
15062  * Fork - LGPL
15063  * <script type="text/javascript">
15064  */
15065 /**
15066  * @class Roo.state.CookieProvider
15067  * @extends Roo.state.Provider
15068  * The default Provider implementation which saves state via cookies.
15069  * <br />Usage:
15070  <pre><code>
15071    var cp = new Roo.state.CookieProvider({
15072        path: "/cgi-bin/",
15073        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15074        domain: "roojs.com"
15075    })
15076    Roo.state.Manager.setProvider(cp);
15077  </code></pre>
15078  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15079  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15080  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15081  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15082  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15083  * domain the page is running on including the 'www' like 'www.roojs.com')
15084  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15085  * @constructor
15086  * Create a new CookieProvider
15087  * @param {Object} config The configuration object
15088  */
15089 Roo.state.CookieProvider = function(config){
15090     Roo.state.CookieProvider.superclass.constructor.call(this);
15091     this.path = "/";
15092     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15093     this.domain = null;
15094     this.secure = false;
15095     Roo.apply(this, config);
15096     this.state = this.readCookies();
15097 };
15098
15099 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15100     // private
15101     set : function(name, value){
15102         if(typeof value == "undefined" || value === null){
15103             this.clear(name);
15104             return;
15105         }
15106         this.setCookie(name, value);
15107         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15108     },
15109
15110     // private
15111     clear : function(name){
15112         this.clearCookie(name);
15113         Roo.state.CookieProvider.superclass.clear.call(this, name);
15114     },
15115
15116     // private
15117     readCookies : function(){
15118         var cookies = {};
15119         var c = document.cookie + ";";
15120         var re = /\s?(.*?)=(.*?);/g;
15121         var matches;
15122         while((matches = re.exec(c)) != null){
15123             var name = matches[1];
15124             var value = matches[2];
15125             if(name && name.substring(0,3) == "ys-"){
15126                 cookies[name.substr(3)] = this.decodeValue(value);
15127             }
15128         }
15129         return cookies;
15130     },
15131
15132     // private
15133     setCookie : function(name, value){
15134         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15135            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15136            ((this.path == null) ? "" : ("; path=" + this.path)) +
15137            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15138            ((this.secure == true) ? "; secure" : "");
15139     },
15140
15141     // private
15142     clearCookie : function(name){
15143         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15144            ((this.path == null) ? "" : ("; path=" + this.path)) +
15145            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15146            ((this.secure == true) ? "; secure" : "");
15147     }
15148 });/*
15149  * Based on:
15150  * Ext JS Library 1.1.1
15151  * Copyright(c) 2006-2007, Ext JS, LLC.
15152  *
15153  * Originally Released Under LGPL - original licence link has changed is not relivant.
15154  *
15155  * Fork - LGPL
15156  * <script type="text/javascript">
15157  */
15158  
15159
15160 /**
15161  * @class Roo.ComponentMgr
15162  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15163  * @singleton
15164  */
15165 Roo.ComponentMgr = function(){
15166     var all = new Roo.util.MixedCollection();
15167
15168     return {
15169         /**
15170          * Registers a component.
15171          * @param {Roo.Component} c The component
15172          */
15173         register : function(c){
15174             all.add(c);
15175         },
15176
15177         /**
15178          * Unregisters a component.
15179          * @param {Roo.Component} c The component
15180          */
15181         unregister : function(c){
15182             all.remove(c);
15183         },
15184
15185         /**
15186          * Returns a component by id
15187          * @param {String} id The component id
15188          */
15189         get : function(id){
15190             return all.get(id);
15191         },
15192
15193         /**
15194          * Registers a function that will be called when a specified component is added to ComponentMgr
15195          * @param {String} id The component id
15196          * @param {Funtction} fn The callback function
15197          * @param {Object} scope The scope of the callback
15198          */
15199         onAvailable : function(id, fn, scope){
15200             all.on("add", function(index, o){
15201                 if(o.id == id){
15202                     fn.call(scope || o, o);
15203                     all.un("add", fn, scope);
15204                 }
15205             });
15206         }
15207     };
15208 }();/*
15209  * Based on:
15210  * Ext JS Library 1.1.1
15211  * Copyright(c) 2006-2007, Ext JS, LLC.
15212  *
15213  * Originally Released Under LGPL - original licence link has changed is not relivant.
15214  *
15215  * Fork - LGPL
15216  * <script type="text/javascript">
15217  */
15218  
15219 /**
15220  * @class Roo.Component
15221  * @extends Roo.util.Observable
15222  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15223  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15224  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15225  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15226  * All visual components (widgets) that require rendering into a layout should subclass Component.
15227  * @constructor
15228  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15229  * 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
15230  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15231  */
15232 Roo.Component = function(config){
15233     config = config || {};
15234     if(config.tagName || config.dom || typeof config == "string"){ // element object
15235         config = {el: config, id: config.id || config};
15236     }
15237     this.initialConfig = config;
15238
15239     Roo.apply(this, config);
15240     this.addEvents({
15241         /**
15242          * @event disable
15243          * Fires after the component is disabled.
15244              * @param {Roo.Component} this
15245              */
15246         disable : true,
15247         /**
15248          * @event enable
15249          * Fires after the component is enabled.
15250              * @param {Roo.Component} this
15251              */
15252         enable : true,
15253         /**
15254          * @event beforeshow
15255          * Fires before the component is shown.  Return false to stop the show.
15256              * @param {Roo.Component} this
15257              */
15258         beforeshow : true,
15259         /**
15260          * @event show
15261          * Fires after the component is shown.
15262              * @param {Roo.Component} this
15263              */
15264         show : true,
15265         /**
15266          * @event beforehide
15267          * Fires before the component is hidden. Return false to stop the hide.
15268              * @param {Roo.Component} this
15269              */
15270         beforehide : true,
15271         /**
15272          * @event hide
15273          * Fires after the component is hidden.
15274              * @param {Roo.Component} this
15275              */
15276         hide : true,
15277         /**
15278          * @event beforerender
15279          * Fires before the component is rendered. Return false to stop the render.
15280              * @param {Roo.Component} this
15281              */
15282         beforerender : true,
15283         /**
15284          * @event render
15285          * Fires after the component is rendered.
15286              * @param {Roo.Component} this
15287              */
15288         render : true,
15289         /**
15290          * @event beforedestroy
15291          * Fires before the component is destroyed. Return false to stop the destroy.
15292              * @param {Roo.Component} this
15293              */
15294         beforedestroy : true,
15295         /**
15296          * @event destroy
15297          * Fires after the component is destroyed.
15298              * @param {Roo.Component} this
15299              */
15300         destroy : true
15301     });
15302     if(!this.id){
15303         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15304     }
15305     Roo.ComponentMgr.register(this);
15306     Roo.Component.superclass.constructor.call(this);
15307     this.initComponent();
15308     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15309         this.render(this.renderTo);
15310         delete this.renderTo;
15311     }
15312 };
15313
15314 /** @private */
15315 Roo.Component.AUTO_ID = 1000;
15316
15317 Roo.extend(Roo.Component, Roo.util.Observable, {
15318     /**
15319      * @scope Roo.Component.prototype
15320      * @type {Boolean}
15321      * true if this component is hidden. Read-only.
15322      */
15323     hidden : false,
15324     /**
15325      * @type {Boolean}
15326      * true if this component is disabled. Read-only.
15327      */
15328     disabled : false,
15329     /**
15330      * @type {Boolean}
15331      * true if this component has been rendered. Read-only.
15332      */
15333     rendered : false,
15334     
15335     /** @cfg {String} disableClass
15336      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15337      */
15338     disabledClass : "x-item-disabled",
15339         /** @cfg {Boolean} allowDomMove
15340          * Whether the component can move the Dom node when rendering (defaults to true).
15341          */
15342     allowDomMove : true,
15343     /** @cfg {String} hideMode (display|visibility)
15344      * How this component should hidden. Supported values are
15345      * "visibility" (css visibility), "offsets" (negative offset position) and
15346      * "display" (css display) - defaults to "display".
15347      */
15348     hideMode: 'display',
15349
15350     /** @private */
15351     ctype : "Roo.Component",
15352
15353     /**
15354      * @cfg {String} actionMode 
15355      * which property holds the element that used for  hide() / show() / disable() / enable()
15356      * default is 'el' 
15357      */
15358     actionMode : "el",
15359
15360     /** @private */
15361     getActionEl : function(){
15362         return this[this.actionMode];
15363     },
15364
15365     initComponent : Roo.emptyFn,
15366     /**
15367      * If this is a lazy rendering component, render it to its container element.
15368      * @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.
15369      */
15370     render : function(container, position){
15371         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15372             if(!container && this.el){
15373                 this.el = Roo.get(this.el);
15374                 container = this.el.dom.parentNode;
15375                 this.allowDomMove = false;
15376             }
15377             this.container = Roo.get(container);
15378             this.rendered = true;
15379             if(position !== undefined){
15380                 if(typeof position == 'number'){
15381                     position = this.container.dom.childNodes[position];
15382                 }else{
15383                     position = Roo.getDom(position);
15384                 }
15385             }
15386             this.onRender(this.container, position || null);
15387             if(this.cls){
15388                 this.el.addClass(this.cls);
15389                 delete this.cls;
15390             }
15391             if(this.style){
15392                 this.el.applyStyles(this.style);
15393                 delete this.style;
15394             }
15395             this.fireEvent("render", this);
15396             this.afterRender(this.container);
15397             if(this.hidden){
15398                 this.hide();
15399             }
15400             if(this.disabled){
15401                 this.disable();
15402             }
15403         }
15404         return this;
15405     },
15406
15407     /** @private */
15408     // default function is not really useful
15409     onRender : function(ct, position){
15410         if(this.el){
15411             this.el = Roo.get(this.el);
15412             if(this.allowDomMove !== false){
15413                 ct.dom.insertBefore(this.el.dom, position);
15414             }
15415         }
15416     },
15417
15418     /** @private */
15419     getAutoCreate : function(){
15420         var cfg = typeof this.autoCreate == "object" ?
15421                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15422         if(this.id && !cfg.id){
15423             cfg.id = this.id;
15424         }
15425         return cfg;
15426     },
15427
15428     /** @private */
15429     afterRender : Roo.emptyFn,
15430
15431     /**
15432      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15433      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15434      */
15435     destroy : function(){
15436         if(this.fireEvent("beforedestroy", this) !== false){
15437             this.purgeListeners();
15438             this.beforeDestroy();
15439             if(this.rendered){
15440                 this.el.removeAllListeners();
15441                 this.el.remove();
15442                 if(this.actionMode == "container"){
15443                     this.container.remove();
15444                 }
15445             }
15446             this.onDestroy();
15447             Roo.ComponentMgr.unregister(this);
15448             this.fireEvent("destroy", this);
15449         }
15450     },
15451
15452         /** @private */
15453     beforeDestroy : function(){
15454
15455     },
15456
15457         /** @private */
15458         onDestroy : function(){
15459
15460     },
15461
15462     /**
15463      * Returns the underlying {@link Roo.Element}.
15464      * @return {Roo.Element} The element
15465      */
15466     getEl : function(){
15467         return this.el;
15468     },
15469
15470     /**
15471      * Returns the id of this component.
15472      * @return {String}
15473      */
15474     getId : function(){
15475         return this.id;
15476     },
15477
15478     /**
15479      * Try to focus this component.
15480      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15481      * @return {Roo.Component} this
15482      */
15483     focus : function(selectText){
15484         if(this.rendered){
15485             this.el.focus();
15486             if(selectText === true){
15487                 this.el.dom.select();
15488             }
15489         }
15490         return this;
15491     },
15492
15493     /** @private */
15494     blur : function(){
15495         if(this.rendered){
15496             this.el.blur();
15497         }
15498         return this;
15499     },
15500
15501     /**
15502      * Disable this component.
15503      * @return {Roo.Component} this
15504      */
15505     disable : function(){
15506         if(this.rendered){
15507             this.onDisable();
15508         }
15509         this.disabled = true;
15510         this.fireEvent("disable", this);
15511         return this;
15512     },
15513
15514         // private
15515     onDisable : function(){
15516         this.getActionEl().addClass(this.disabledClass);
15517         this.el.dom.disabled = true;
15518     },
15519
15520     /**
15521      * Enable this component.
15522      * @return {Roo.Component} this
15523      */
15524     enable : function(){
15525         if(this.rendered){
15526             this.onEnable();
15527         }
15528         this.disabled = false;
15529         this.fireEvent("enable", this);
15530         return this;
15531     },
15532
15533         // private
15534     onEnable : function(){
15535         this.getActionEl().removeClass(this.disabledClass);
15536         this.el.dom.disabled = false;
15537     },
15538
15539     /**
15540      * Convenience function for setting disabled/enabled by boolean.
15541      * @param {Boolean} disabled
15542      */
15543     setDisabled : function(disabled){
15544         this[disabled ? "disable" : "enable"]();
15545     },
15546
15547     /**
15548      * Show this component.
15549      * @return {Roo.Component} this
15550      */
15551     show: function(){
15552         if(this.fireEvent("beforeshow", this) !== false){
15553             this.hidden = false;
15554             if(this.rendered){
15555                 this.onShow();
15556             }
15557             this.fireEvent("show", this);
15558         }
15559         return this;
15560     },
15561
15562     // private
15563     onShow : function(){
15564         var ae = this.getActionEl();
15565         if(this.hideMode == 'visibility'){
15566             ae.dom.style.visibility = "visible";
15567         }else if(this.hideMode == 'offsets'){
15568             ae.removeClass('x-hidden');
15569         }else{
15570             ae.dom.style.display = "";
15571         }
15572     },
15573
15574     /**
15575      * Hide this component.
15576      * @return {Roo.Component} this
15577      */
15578     hide: function(){
15579         if(this.fireEvent("beforehide", this) !== false){
15580             this.hidden = true;
15581             if(this.rendered){
15582                 this.onHide();
15583             }
15584             this.fireEvent("hide", this);
15585         }
15586         return this;
15587     },
15588
15589     // private
15590     onHide : function(){
15591         var ae = this.getActionEl();
15592         if(this.hideMode == 'visibility'){
15593             ae.dom.style.visibility = "hidden";
15594         }else if(this.hideMode == 'offsets'){
15595             ae.addClass('x-hidden');
15596         }else{
15597             ae.dom.style.display = "none";
15598         }
15599     },
15600
15601     /**
15602      * Convenience function to hide or show this component by boolean.
15603      * @param {Boolean} visible True to show, false to hide
15604      * @return {Roo.Component} this
15605      */
15606     setVisible: function(visible){
15607         if(visible) {
15608             this.show();
15609         }else{
15610             this.hide();
15611         }
15612         return this;
15613     },
15614
15615     /**
15616      * Returns true if this component is visible.
15617      */
15618     isVisible : function(){
15619         return this.getActionEl().isVisible();
15620     },
15621
15622     cloneConfig : function(overrides){
15623         overrides = overrides || {};
15624         var id = overrides.id || Roo.id();
15625         var cfg = Roo.applyIf(overrides, this.initialConfig);
15626         cfg.id = id; // prevent dup id
15627         return new this.constructor(cfg);
15628     }
15629 });/*
15630  * Based on:
15631  * Ext JS Library 1.1.1
15632  * Copyright(c) 2006-2007, Ext JS, LLC.
15633  *
15634  * Originally Released Under LGPL - original licence link has changed is not relivant.
15635  *
15636  * Fork - LGPL
15637  * <script type="text/javascript">
15638  */
15639
15640 /**
15641  * @class Roo.BoxComponent
15642  * @extends Roo.Component
15643  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15644  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15645  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15646  * layout containers.
15647  * @constructor
15648  * @param {Roo.Element/String/Object} config The configuration options.
15649  */
15650 Roo.BoxComponent = function(config){
15651     Roo.Component.call(this, config);
15652     this.addEvents({
15653         /**
15654          * @event resize
15655          * Fires after the component is resized.
15656              * @param {Roo.Component} this
15657              * @param {Number} adjWidth The box-adjusted width that was set
15658              * @param {Number} adjHeight The box-adjusted height that was set
15659              * @param {Number} rawWidth The width that was originally specified
15660              * @param {Number} rawHeight The height that was originally specified
15661              */
15662         resize : true,
15663         /**
15664          * @event move
15665          * Fires after the component is moved.
15666              * @param {Roo.Component} this
15667              * @param {Number} x The new x position
15668              * @param {Number} y The new y position
15669              */
15670         move : true
15671     });
15672 };
15673
15674 Roo.extend(Roo.BoxComponent, Roo.Component, {
15675     // private, set in afterRender to signify that the component has been rendered
15676     boxReady : false,
15677     // private, used to defer height settings to subclasses
15678     deferHeight: false,
15679     /** @cfg {Number} width
15680      * width (optional) size of component
15681      */
15682      /** @cfg {Number} height
15683      * height (optional) size of component
15684      */
15685      
15686     /**
15687      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15688      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15689      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15690      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15691      * @return {Roo.BoxComponent} this
15692      */
15693     setSize : function(w, h){
15694         // support for standard size objects
15695         if(typeof w == 'object'){
15696             h = w.height;
15697             w = w.width;
15698         }
15699         // not rendered
15700         if(!this.boxReady){
15701             this.width = w;
15702             this.height = h;
15703             return this;
15704         }
15705
15706         // prevent recalcs when not needed
15707         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15708             return this;
15709         }
15710         this.lastSize = {width: w, height: h};
15711
15712         var adj = this.adjustSize(w, h);
15713         var aw = adj.width, ah = adj.height;
15714         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15715             var rz = this.getResizeEl();
15716             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15717                 rz.setSize(aw, ah);
15718             }else if(!this.deferHeight && ah !== undefined){
15719                 rz.setHeight(ah);
15720             }else if(aw !== undefined){
15721                 rz.setWidth(aw);
15722             }
15723             this.onResize(aw, ah, w, h);
15724             this.fireEvent('resize', this, aw, ah, w, h);
15725         }
15726         return this;
15727     },
15728
15729     /**
15730      * Gets the current size of the component's underlying element.
15731      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15732      */
15733     getSize : function(){
15734         return this.el.getSize();
15735     },
15736
15737     /**
15738      * Gets the current XY position of the component's underlying element.
15739      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15740      * @return {Array} The XY position of the element (e.g., [100, 200])
15741      */
15742     getPosition : function(local){
15743         if(local === true){
15744             return [this.el.getLeft(true), this.el.getTop(true)];
15745         }
15746         return this.xy || this.el.getXY();
15747     },
15748
15749     /**
15750      * Gets the current box measurements of the component's underlying element.
15751      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15752      * @returns {Object} box An object in the format {x, y, width, height}
15753      */
15754     getBox : function(local){
15755         var s = this.el.getSize();
15756         if(local){
15757             s.x = this.el.getLeft(true);
15758             s.y = this.el.getTop(true);
15759         }else{
15760             var xy = this.xy || this.el.getXY();
15761             s.x = xy[0];
15762             s.y = xy[1];
15763         }
15764         return s;
15765     },
15766
15767     /**
15768      * Sets the current box measurements of the component's underlying element.
15769      * @param {Object} box An object in the format {x, y, width, height}
15770      * @returns {Roo.BoxComponent} this
15771      */
15772     updateBox : function(box){
15773         this.setSize(box.width, box.height);
15774         this.setPagePosition(box.x, box.y);
15775         return this;
15776     },
15777
15778     // protected
15779     getResizeEl : function(){
15780         return this.resizeEl || this.el;
15781     },
15782
15783     // protected
15784     getPositionEl : function(){
15785         return this.positionEl || this.el;
15786     },
15787
15788     /**
15789      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15790      * This method fires the move event.
15791      * @param {Number} left The new left
15792      * @param {Number} top The new top
15793      * @returns {Roo.BoxComponent} this
15794      */
15795     setPosition : function(x, y){
15796         this.x = x;
15797         this.y = y;
15798         if(!this.boxReady){
15799             return this;
15800         }
15801         var adj = this.adjustPosition(x, y);
15802         var ax = adj.x, ay = adj.y;
15803
15804         var el = this.getPositionEl();
15805         if(ax !== undefined || ay !== undefined){
15806             if(ax !== undefined && ay !== undefined){
15807                 el.setLeftTop(ax, ay);
15808             }else if(ax !== undefined){
15809                 el.setLeft(ax);
15810             }else if(ay !== undefined){
15811                 el.setTop(ay);
15812             }
15813             this.onPosition(ax, ay);
15814             this.fireEvent('move', this, ax, ay);
15815         }
15816         return this;
15817     },
15818
15819     /**
15820      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15821      * This method fires the move event.
15822      * @param {Number} x The new x position
15823      * @param {Number} y The new y position
15824      * @returns {Roo.BoxComponent} this
15825      */
15826     setPagePosition : function(x, y){
15827         this.pageX = x;
15828         this.pageY = y;
15829         if(!this.boxReady){
15830             return;
15831         }
15832         if(x === undefined || y === undefined){ // cannot translate undefined points
15833             return;
15834         }
15835         var p = this.el.translatePoints(x, y);
15836         this.setPosition(p.left, p.top);
15837         return this;
15838     },
15839
15840     // private
15841     onRender : function(ct, position){
15842         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15843         if(this.resizeEl){
15844             this.resizeEl = Roo.get(this.resizeEl);
15845         }
15846         if(this.positionEl){
15847             this.positionEl = Roo.get(this.positionEl);
15848         }
15849     },
15850
15851     // private
15852     afterRender : function(){
15853         Roo.BoxComponent.superclass.afterRender.call(this);
15854         this.boxReady = true;
15855         this.setSize(this.width, this.height);
15856         if(this.x || this.y){
15857             this.setPosition(this.x, this.y);
15858         }
15859         if(this.pageX || this.pageY){
15860             this.setPagePosition(this.pageX, this.pageY);
15861         }
15862     },
15863
15864     /**
15865      * Force the component's size to recalculate based on the underlying element's current height and width.
15866      * @returns {Roo.BoxComponent} this
15867      */
15868     syncSize : function(){
15869         delete this.lastSize;
15870         this.setSize(this.el.getWidth(), this.el.getHeight());
15871         return this;
15872     },
15873
15874     /**
15875      * Called after the component is resized, this method is empty by default but can be implemented by any
15876      * subclass that needs to perform custom logic after a resize occurs.
15877      * @param {Number} adjWidth The box-adjusted width that was set
15878      * @param {Number} adjHeight The box-adjusted height that was set
15879      * @param {Number} rawWidth The width that was originally specified
15880      * @param {Number} rawHeight The height that was originally specified
15881      */
15882     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15883
15884     },
15885
15886     /**
15887      * Called after the component is moved, this method is empty by default but can be implemented by any
15888      * subclass that needs to perform custom logic after a move occurs.
15889      * @param {Number} x The new x position
15890      * @param {Number} y The new y position
15891      */
15892     onPosition : function(x, y){
15893
15894     },
15895
15896     // private
15897     adjustSize : function(w, h){
15898         if(this.autoWidth){
15899             w = 'auto';
15900         }
15901         if(this.autoHeight){
15902             h = 'auto';
15903         }
15904         return {width : w, height: h};
15905     },
15906
15907     // private
15908     adjustPosition : function(x, y){
15909         return {x : x, y: y};
15910     }
15911 });/*
15912  * Original code for Roojs - LGPL
15913  * <script type="text/javascript">
15914  */
15915  
15916 /**
15917  * @class Roo.XComponent
15918  * A delayed Element creator...
15919  * Or a way to group chunks of interface together.
15920  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15921  *  used in conjunction with XComponent.build() it will create an instance of each element,
15922  *  then call addxtype() to build the User interface.
15923  * 
15924  * Mypart.xyx = new Roo.XComponent({
15925
15926     parent : 'Mypart.xyz', // empty == document.element.!!
15927     order : '001',
15928     name : 'xxxx'
15929     region : 'xxxx'
15930     disabled : function() {} 
15931      
15932     tree : function() { // return an tree of xtype declared components
15933         var MODULE = this;
15934         return 
15935         {
15936             xtype : 'NestedLayoutPanel',
15937             // technicall
15938         }
15939      ]
15940  *})
15941  *
15942  *
15943  * It can be used to build a big heiracy, with parent etc.
15944  * or you can just use this to render a single compoent to a dom element
15945  * MYPART.render(Roo.Element | String(id) | dom_element )
15946  *
15947  *
15948  * Usage patterns.
15949  *
15950  * Classic Roo
15951  *
15952  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15953  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15954  *
15955  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15956  *
15957  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15958  * - if mulitple topModules exist, the last one is defined as the top module.
15959  *
15960  * Embeded Roo
15961  * 
15962  * When the top level or multiple modules are to embedded into a existing HTML page,
15963  * the parent element can container '#id' of the element where the module will be drawn.
15964  *
15965  * Bootstrap Roo
15966  *
15967  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15968  * it relies more on a include mechanism, where sub modules are included into an outer page.
15969  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15970  * 
15971  * Bootstrap Roo Included elements
15972  *
15973  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15974  * hence confusing the component builder as it thinks there are multiple top level elements. 
15975  *
15976  * 
15977  * 
15978  * @extends Roo.util.Observable
15979  * @constructor
15980  * @param cfg {Object} configuration of component
15981  * 
15982  */
15983 Roo.XComponent = function(cfg) {
15984     Roo.apply(this, cfg);
15985     this.addEvents({ 
15986         /**
15987              * @event built
15988              * Fires when this the componnt is built
15989              * @param {Roo.XComponent} c the component
15990              */
15991         'built' : true
15992         
15993     });
15994     this.region = this.region || 'center'; // default..
15995     Roo.XComponent.register(this);
15996     this.modules = false;
15997     this.el = false; // where the layout goes..
15998     
15999     
16000 }
16001 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16002     /**
16003      * @property el
16004      * The created element (with Roo.factory())
16005      * @type {Roo.Layout}
16006      */
16007     el  : false,
16008     
16009     /**
16010      * @property el
16011      * for BC  - use el in new code
16012      * @type {Roo.Layout}
16013      */
16014     panel : false,
16015     
16016     /**
16017      * @property layout
16018      * for BC  - use el in new code
16019      * @type {Roo.Layout}
16020      */
16021     layout : false,
16022     
16023      /**
16024      * @cfg {Function|boolean} disabled
16025      * If this module is disabled by some rule, return true from the funtion
16026      */
16027     disabled : false,
16028     
16029     /**
16030      * @cfg {String} parent 
16031      * Name of parent element which it get xtype added to..
16032      */
16033     parent: false,
16034     
16035     /**
16036      * @cfg {String} order
16037      * Used to set the order in which elements are created (usefull for multiple tabs)
16038      */
16039     
16040     order : false,
16041     /**
16042      * @cfg {String} name
16043      * String to display while loading.
16044      */
16045     name : false,
16046     /**
16047      * @cfg {String} region
16048      * Region to render component to (defaults to center)
16049      */
16050     region : 'center',
16051     
16052     /**
16053      * @cfg {Array} items
16054      * A single item array - the first element is the root of the tree..
16055      * It's done this way to stay compatible with the Xtype system...
16056      */
16057     items : false,
16058     
16059     /**
16060      * @property _tree
16061      * The method that retuns the tree of parts that make up this compoennt 
16062      * @type {function}
16063      */
16064     _tree  : false,
16065     
16066      /**
16067      * render
16068      * render element to dom or tree
16069      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16070      */
16071     
16072     render : function(el)
16073     {
16074         
16075         el = el || false;
16076         var hp = this.parent ? 1 : 0;
16077         Roo.debug &&  Roo.log(this);
16078         
16079         var tree = this._tree ? this._tree() : this.tree();
16080
16081         
16082         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16083             // if parent is a '#.....' string, then let's use that..
16084             var ename = this.parent.substr(1);
16085             this.parent = false;
16086             Roo.debug && Roo.log(ename);
16087             switch (ename) {
16088                 case 'bootstrap-body':
16089                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16090                         // this is the BorderLayout standard?
16091                        this.parent = { el : true };
16092                        break;
16093                     }
16094                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16095                         // need to insert stuff...
16096                         this.parent =  {
16097                              el : new Roo.bootstrap.layout.Border({
16098                                  el : document.body, 
16099                      
16100                                  center: {
16101                                     titlebar: false,
16102                                     autoScroll:false,
16103                                     closeOnTab: true,
16104                                     tabPosition: 'top',
16105                                       //resizeTabs: true,
16106                                     alwaysShowTabs: true,
16107                                     hideTabs: false
16108                                      //minTabWidth: 140
16109                                  }
16110                              })
16111                         
16112                          };
16113                          break;
16114                     }
16115                          
16116                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16117                         this.parent = { el :  new  Roo.bootstrap.Body() };
16118                         Roo.debug && Roo.log("setting el to doc body");
16119                          
16120                     } else {
16121                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16122                     }
16123                     break;
16124                 case 'bootstrap':
16125                     this.parent = { el : true};
16126                     // fall through
16127                 default:
16128                     el = Roo.get(ename);
16129                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16130                         this.parent = { el : true};
16131                     }
16132                     
16133                     break;
16134             }
16135                 
16136             
16137             if (!el && !this.parent) {
16138                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16139                 return;
16140             }
16141         }
16142         
16143         Roo.debug && Roo.log("EL:");
16144         Roo.debug && Roo.log(el);
16145         Roo.debug && Roo.log("this.parent.el:");
16146         Roo.debug && Roo.log(this.parent.el);
16147         
16148
16149         // altertive root elements ??? - we need a better way to indicate these.
16150         var is_alt = Roo.XComponent.is_alt ||
16151                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16152                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16153                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16154         
16155         
16156         
16157         if (!this.parent && is_alt) {
16158             //el = Roo.get(document.body);
16159             this.parent = { el : true };
16160         }
16161             
16162             
16163         
16164         if (!this.parent) {
16165             
16166             Roo.debug && Roo.log("no parent - creating one");
16167             
16168             el = el ? Roo.get(el) : false;      
16169             
16170             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16171                 
16172                 this.parent =  {
16173                     el : new Roo.bootstrap.layout.Border({
16174                         el: el || document.body,
16175                     
16176                         center: {
16177                             titlebar: false,
16178                             autoScroll:false,
16179                             closeOnTab: true,
16180                             tabPosition: 'top',
16181                              //resizeTabs: true,
16182                             alwaysShowTabs: false,
16183                             hideTabs: true,
16184                             minTabWidth: 140,
16185                             overflow: 'visible'
16186                          }
16187                      })
16188                 };
16189             } else {
16190             
16191                 // it's a top level one..
16192                 this.parent =  {
16193                     el : new Roo.BorderLayout(el || document.body, {
16194                         center: {
16195                             titlebar: false,
16196                             autoScroll:false,
16197                             closeOnTab: true,
16198                             tabPosition: 'top',
16199                              //resizeTabs: true,
16200                             alwaysShowTabs: el && hp? false :  true,
16201                             hideTabs: el || !hp ? true :  false,
16202                             minTabWidth: 140
16203                          }
16204                     })
16205                 };
16206             }
16207         }
16208         
16209         if (!this.parent.el) {
16210                 // probably an old style ctor, which has been disabled.
16211                 return;
16212
16213         }
16214                 // The 'tree' method is  '_tree now' 
16215             
16216         tree.region = tree.region || this.region;
16217         var is_body = false;
16218         if (this.parent.el === true) {
16219             // bootstrap... - body..
16220             if (el) {
16221                 tree.el = el;
16222             }
16223             this.parent.el = Roo.factory(tree);
16224             is_body = true;
16225         }
16226         
16227         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16228         this.fireEvent('built', this);
16229         
16230         this.panel = this.el;
16231         this.layout = this.panel.layout;
16232         this.parentLayout = this.parent.layout  || false;  
16233          
16234     }
16235     
16236 });
16237
16238 Roo.apply(Roo.XComponent, {
16239     /**
16240      * @property  hideProgress
16241      * true to disable the building progress bar.. usefull on single page renders.
16242      * @type Boolean
16243      */
16244     hideProgress : false,
16245     /**
16246      * @property  buildCompleted
16247      * True when the builder has completed building the interface.
16248      * @type Boolean
16249      */
16250     buildCompleted : false,
16251      
16252     /**
16253      * @property  topModule
16254      * the upper most module - uses document.element as it's constructor.
16255      * @type Object
16256      */
16257      
16258     topModule  : false,
16259       
16260     /**
16261      * @property  modules
16262      * array of modules to be created by registration system.
16263      * @type {Array} of Roo.XComponent
16264      */
16265     
16266     modules : [],
16267     /**
16268      * @property  elmodules
16269      * array of modules to be created by which use #ID 
16270      * @type {Array} of Roo.XComponent
16271      */
16272      
16273     elmodules : [],
16274
16275      /**
16276      * @property  is_alt
16277      * Is an alternative Root - normally used by bootstrap or other systems,
16278      *    where the top element in the tree can wrap 'body' 
16279      * @type {boolean}  (default false)
16280      */
16281      
16282     is_alt : false,
16283     /**
16284      * @property  build_from_html
16285      * Build elements from html - used by bootstrap HTML stuff 
16286      *    - this is cleared after build is completed
16287      * @type {boolean}    (default false)
16288      */
16289      
16290     build_from_html : false,
16291     /**
16292      * Register components to be built later.
16293      *
16294      * This solves the following issues
16295      * - Building is not done on page load, but after an authentication process has occured.
16296      * - Interface elements are registered on page load
16297      * - Parent Interface elements may not be loaded before child, so this handles that..
16298      * 
16299      *
16300      * example:
16301      * 
16302      * MyApp.register({
16303           order : '000001',
16304           module : 'Pman.Tab.projectMgr',
16305           region : 'center',
16306           parent : 'Pman.layout',
16307           disabled : false,  // or use a function..
16308         })
16309      
16310      * * @param {Object} details about module
16311      */
16312     register : function(obj) {
16313                 
16314         Roo.XComponent.event.fireEvent('register', obj);
16315         switch(typeof(obj.disabled) ) {
16316                 
16317             case 'undefined':
16318                 break;
16319             
16320             case 'function':
16321                 if ( obj.disabled() ) {
16322                         return;
16323                 }
16324                 break;
16325             
16326             default:
16327                 if (obj.disabled) {
16328                         return;
16329                 }
16330                 break;
16331         }
16332                 
16333         this.modules.push(obj);
16334          
16335     },
16336     /**
16337      * convert a string to an object..
16338      * eg. 'AAA.BBB' -> finds AAA.BBB
16339
16340      */
16341     
16342     toObject : function(str)
16343     {
16344         if (!str || typeof(str) == 'object') {
16345             return str;
16346         }
16347         if (str.substring(0,1) == '#') {
16348             return str;
16349         }
16350
16351         var ar = str.split('.');
16352         var rt, o;
16353         rt = ar.shift();
16354             /** eval:var:o */
16355         try {
16356             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16357         } catch (e) {
16358             throw "Module not found : " + str;
16359         }
16360         
16361         if (o === false) {
16362             throw "Module not found : " + str;
16363         }
16364         Roo.each(ar, function(e) {
16365             if (typeof(o[e]) == 'undefined') {
16366                 throw "Module not found : " + str;
16367             }
16368             o = o[e];
16369         });
16370         
16371         return o;
16372         
16373     },
16374     
16375     
16376     /**
16377      * move modules into their correct place in the tree..
16378      * 
16379      */
16380     preBuild : function ()
16381     {
16382         var _t = this;
16383         Roo.each(this.modules , function (obj)
16384         {
16385             Roo.XComponent.event.fireEvent('beforebuild', obj);
16386             
16387             var opar = obj.parent;
16388             try { 
16389                 obj.parent = this.toObject(opar);
16390             } catch(e) {
16391                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16392                 return;
16393             }
16394             
16395             if (!obj.parent) {
16396                 Roo.debug && Roo.log("GOT top level module");
16397                 Roo.debug && Roo.log(obj);
16398                 obj.modules = new Roo.util.MixedCollection(false, 
16399                     function(o) { return o.order + '' }
16400                 );
16401                 this.topModule = obj;
16402                 return;
16403             }
16404                         // parent is a string (usually a dom element name..)
16405             if (typeof(obj.parent) == 'string') {
16406                 this.elmodules.push(obj);
16407                 return;
16408             }
16409             if (obj.parent.constructor != Roo.XComponent) {
16410                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16411             }
16412             if (!obj.parent.modules) {
16413                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16414                     function(o) { return o.order + '' }
16415                 );
16416             }
16417             if (obj.parent.disabled) {
16418                 obj.disabled = true;
16419             }
16420             obj.parent.modules.add(obj);
16421         }, this);
16422     },
16423     
16424      /**
16425      * make a list of modules to build.
16426      * @return {Array} list of modules. 
16427      */ 
16428     
16429     buildOrder : function()
16430     {
16431         var _this = this;
16432         var cmp = function(a,b) {   
16433             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16434         };
16435         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16436             throw "No top level modules to build";
16437         }
16438         
16439         // make a flat list in order of modules to build.
16440         var mods = this.topModule ? [ this.topModule ] : [];
16441                 
16442         
16443         // elmodules (is a list of DOM based modules )
16444         Roo.each(this.elmodules, function(e) {
16445             mods.push(e);
16446             if (!this.topModule &&
16447                 typeof(e.parent) == 'string' &&
16448                 e.parent.substring(0,1) == '#' &&
16449                 Roo.get(e.parent.substr(1))
16450                ) {
16451                 
16452                 _this.topModule = e;
16453             }
16454             
16455         });
16456
16457         
16458         // add modules to their parents..
16459         var addMod = function(m) {
16460             Roo.debug && Roo.log("build Order: add: " + m.name);
16461                 
16462             mods.push(m);
16463             if (m.modules && !m.disabled) {
16464                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16465                 m.modules.keySort('ASC',  cmp );
16466                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16467     
16468                 m.modules.each(addMod);
16469             } else {
16470                 Roo.debug && Roo.log("build Order: no child modules");
16471             }
16472             // not sure if this is used any more..
16473             if (m.finalize) {
16474                 m.finalize.name = m.name + " (clean up) ";
16475                 mods.push(m.finalize);
16476             }
16477             
16478         }
16479         if (this.topModule && this.topModule.modules) { 
16480             this.topModule.modules.keySort('ASC',  cmp );
16481             this.topModule.modules.each(addMod);
16482         } 
16483         return mods;
16484     },
16485     
16486      /**
16487      * Build the registered modules.
16488      * @param {Object} parent element.
16489      * @param {Function} optional method to call after module has been added.
16490      * 
16491      */ 
16492    
16493     build : function(opts) 
16494     {
16495         
16496         if (typeof(opts) != 'undefined') {
16497             Roo.apply(this,opts);
16498         }
16499         
16500         this.preBuild();
16501         var mods = this.buildOrder();
16502       
16503         //this.allmods = mods;
16504         //Roo.debug && Roo.log(mods);
16505         //return;
16506         if (!mods.length) { // should not happen
16507             throw "NO modules!!!";
16508         }
16509         
16510         
16511         var msg = "Building Interface...";
16512         // flash it up as modal - so we store the mask!?
16513         if (!this.hideProgress && Roo.MessageBox) {
16514             Roo.MessageBox.show({ title: 'loading' });
16515             Roo.MessageBox.show({
16516                title: "Please wait...",
16517                msg: msg,
16518                width:450,
16519                progress:true,
16520                closable:false,
16521                modal: false
16522               
16523             });
16524         }
16525         var total = mods.length;
16526         
16527         var _this = this;
16528         var progressRun = function() {
16529             if (!mods.length) {
16530                 Roo.debug && Roo.log('hide?');
16531                 if (!this.hideProgress && Roo.MessageBox) {
16532                     Roo.MessageBox.hide();
16533                 }
16534                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16535                 
16536                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16537                 
16538                 // THE END...
16539                 return false;   
16540             }
16541             
16542             var m = mods.shift();
16543             
16544             
16545             Roo.debug && Roo.log(m);
16546             // not sure if this is supported any more.. - modules that are are just function
16547             if (typeof(m) == 'function') { 
16548                 m.call(this);
16549                 return progressRun.defer(10, _this);
16550             } 
16551             
16552             
16553             msg = "Building Interface " + (total  - mods.length) + 
16554                     " of " + total + 
16555                     (m.name ? (' - ' + m.name) : '');
16556                         Roo.debug && Roo.log(msg);
16557             if (!_this.hideProgress &&  Roo.MessageBox) { 
16558                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16559             }
16560             
16561          
16562             // is the module disabled?
16563             var disabled = (typeof(m.disabled) == 'function') ?
16564                 m.disabled.call(m.module.disabled) : m.disabled;    
16565             
16566             
16567             if (disabled) {
16568                 return progressRun(); // we do not update the display!
16569             }
16570             
16571             // now build 
16572             
16573                         
16574                         
16575             m.render();
16576             // it's 10 on top level, and 1 on others??? why...
16577             return progressRun.defer(10, _this);
16578              
16579         }
16580         progressRun.defer(1, _this);
16581      
16582         
16583         
16584     },
16585         
16586         
16587         /**
16588          * Event Object.
16589          *
16590          *
16591          */
16592         event: false, 
16593     /**
16594          * wrapper for event.on - aliased later..  
16595          * Typically use to register a event handler for register:
16596          *
16597          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16598          *
16599          */
16600     on : false
16601    
16602     
16603     
16604 });
16605
16606 Roo.XComponent.event = new Roo.util.Observable({
16607                 events : { 
16608                         /**
16609                          * @event register
16610                          * Fires when an Component is registered,
16611                          * set the disable property on the Component to stop registration.
16612                          * @param {Roo.XComponent} c the component being registerd.
16613                          * 
16614                          */
16615                         'register' : true,
16616             /**
16617                          * @event beforebuild
16618                          * Fires before each Component is built
16619                          * can be used to apply permissions.
16620                          * @param {Roo.XComponent} c the component being registerd.
16621                          * 
16622                          */
16623                         'beforebuild' : true,
16624                         /**
16625                          * @event buildcomplete
16626                          * Fires on the top level element when all elements have been built
16627                          * @param {Roo.XComponent} the top level component.
16628                          */
16629                         'buildcomplete' : true
16630                         
16631                 }
16632 });
16633
16634 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16635  //
16636  /**
16637  * marked - a markdown parser
16638  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16639  * https://github.com/chjj/marked
16640  */
16641
16642
16643 /**
16644  *
16645  * Roo.Markdown - is a very crude wrapper around marked..
16646  *
16647  * usage:
16648  * 
16649  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16650  * 
16651  * Note: move the sample code to the bottom of this
16652  * file before uncommenting it.
16653  *
16654  */
16655
16656 Roo.Markdown = {};
16657 Roo.Markdown.toHtml = function(text) {
16658     
16659     var c = new Roo.Markdown.marked.setOptions({
16660             renderer: new Roo.Markdown.marked.Renderer(),
16661             gfm: true,
16662             tables: true,
16663             breaks: false,
16664             pedantic: false,
16665             sanitize: false,
16666             smartLists: true,
16667             smartypants: false
16668           });
16669     // A FEW HACKS!!?
16670     
16671     text = text.replace(/\\\n/g,' ');
16672     return Roo.Markdown.marked(text);
16673 };
16674 //
16675 // converter
16676 //
16677 // Wraps all "globals" so that the only thing
16678 // exposed is makeHtml().
16679 //
16680 (function() {
16681     
16682     /**
16683      * Block-Level Grammar
16684      */
16685     
16686     var block = {
16687       newline: /^\n+/,
16688       code: /^( {4}[^\n]+\n*)+/,
16689       fences: noop,
16690       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16691       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16692       nptable: noop,
16693       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16694       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16695       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16696       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16697       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16698       table: noop,
16699       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16700       text: /^[^\n]+/
16701     };
16702     
16703     block.bullet = /(?:[*+-]|\d+\.)/;
16704     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16705     block.item = replace(block.item, 'gm')
16706       (/bull/g, block.bullet)
16707       ();
16708     
16709     block.list = replace(block.list)
16710       (/bull/g, block.bullet)
16711       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16712       ('def', '\\n+(?=' + block.def.source + ')')
16713       ();
16714     
16715     block.blockquote = replace(block.blockquote)
16716       ('def', block.def)
16717       ();
16718     
16719     block._tag = '(?!(?:'
16720       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16721       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16722       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16723     
16724     block.html = replace(block.html)
16725       ('comment', /<!--[\s\S]*?-->/)
16726       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16727       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16728       (/tag/g, block._tag)
16729       ();
16730     
16731     block.paragraph = replace(block.paragraph)
16732       ('hr', block.hr)
16733       ('heading', block.heading)
16734       ('lheading', block.lheading)
16735       ('blockquote', block.blockquote)
16736       ('tag', '<' + block._tag)
16737       ('def', block.def)
16738       ();
16739     
16740     /**
16741      * Normal Block Grammar
16742      */
16743     
16744     block.normal = merge({}, block);
16745     
16746     /**
16747      * GFM Block Grammar
16748      */
16749     
16750     block.gfm = merge({}, block.normal, {
16751       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16752       paragraph: /^/,
16753       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16754     });
16755     
16756     block.gfm.paragraph = replace(block.paragraph)
16757       ('(?!', '(?!'
16758         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16759         + block.list.source.replace('\\1', '\\3') + '|')
16760       ();
16761     
16762     /**
16763      * GFM + Tables Block Grammar
16764      */
16765     
16766     block.tables = merge({}, block.gfm, {
16767       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16768       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16769     });
16770     
16771     /**
16772      * Block Lexer
16773      */
16774     
16775     function Lexer(options) {
16776       this.tokens = [];
16777       this.tokens.links = {};
16778       this.options = options || marked.defaults;
16779       this.rules = block.normal;
16780     
16781       if (this.options.gfm) {
16782         if (this.options.tables) {
16783           this.rules = block.tables;
16784         } else {
16785           this.rules = block.gfm;
16786         }
16787       }
16788     }
16789     
16790     /**
16791      * Expose Block Rules
16792      */
16793     
16794     Lexer.rules = block;
16795     
16796     /**
16797      * Static Lex Method
16798      */
16799     
16800     Lexer.lex = function(src, options) {
16801       var lexer = new Lexer(options);
16802       return lexer.lex(src);
16803     };
16804     
16805     /**
16806      * Preprocessing
16807      */
16808     
16809     Lexer.prototype.lex = function(src) {
16810       src = src
16811         .replace(/\r\n|\r/g, '\n')
16812         .replace(/\t/g, '    ')
16813         .replace(/\u00a0/g, ' ')
16814         .replace(/\u2424/g, '\n');
16815     
16816       return this.token(src, true);
16817     };
16818     
16819     /**
16820      * Lexing
16821      */
16822     
16823     Lexer.prototype.token = function(src, top, bq) {
16824       var src = src.replace(/^ +$/gm, '')
16825         , next
16826         , loose
16827         , cap
16828         , bull
16829         , b
16830         , item
16831         , space
16832         , i
16833         , l;
16834     
16835       while (src) {
16836         // newline
16837         if (cap = this.rules.newline.exec(src)) {
16838           src = src.substring(cap[0].length);
16839           if (cap[0].length > 1) {
16840             this.tokens.push({
16841               type: 'space'
16842             });
16843           }
16844         }
16845     
16846         // code
16847         if (cap = this.rules.code.exec(src)) {
16848           src = src.substring(cap[0].length);
16849           cap = cap[0].replace(/^ {4}/gm, '');
16850           this.tokens.push({
16851             type: 'code',
16852             text: !this.options.pedantic
16853               ? cap.replace(/\n+$/, '')
16854               : cap
16855           });
16856           continue;
16857         }
16858     
16859         // fences (gfm)
16860         if (cap = this.rules.fences.exec(src)) {
16861           src = src.substring(cap[0].length);
16862           this.tokens.push({
16863             type: 'code',
16864             lang: cap[2],
16865             text: cap[3] || ''
16866           });
16867           continue;
16868         }
16869     
16870         // heading
16871         if (cap = this.rules.heading.exec(src)) {
16872           src = src.substring(cap[0].length);
16873           this.tokens.push({
16874             type: 'heading',
16875             depth: cap[1].length,
16876             text: cap[2]
16877           });
16878           continue;
16879         }
16880     
16881         // table no leading pipe (gfm)
16882         if (top && (cap = this.rules.nptable.exec(src))) {
16883           src = src.substring(cap[0].length);
16884     
16885           item = {
16886             type: 'table',
16887             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16888             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16889             cells: cap[3].replace(/\n$/, '').split('\n')
16890           };
16891     
16892           for (i = 0; i < item.align.length; i++) {
16893             if (/^ *-+: *$/.test(item.align[i])) {
16894               item.align[i] = 'right';
16895             } else if (/^ *:-+: *$/.test(item.align[i])) {
16896               item.align[i] = 'center';
16897             } else if (/^ *:-+ *$/.test(item.align[i])) {
16898               item.align[i] = 'left';
16899             } else {
16900               item.align[i] = null;
16901             }
16902           }
16903     
16904           for (i = 0; i < item.cells.length; i++) {
16905             item.cells[i] = item.cells[i].split(/ *\| */);
16906           }
16907     
16908           this.tokens.push(item);
16909     
16910           continue;
16911         }
16912     
16913         // lheading
16914         if (cap = this.rules.lheading.exec(src)) {
16915           src = src.substring(cap[0].length);
16916           this.tokens.push({
16917             type: 'heading',
16918             depth: cap[2] === '=' ? 1 : 2,
16919             text: cap[1]
16920           });
16921           continue;
16922         }
16923     
16924         // hr
16925         if (cap = this.rules.hr.exec(src)) {
16926           src = src.substring(cap[0].length);
16927           this.tokens.push({
16928             type: 'hr'
16929           });
16930           continue;
16931         }
16932     
16933         // blockquote
16934         if (cap = this.rules.blockquote.exec(src)) {
16935           src = src.substring(cap[0].length);
16936     
16937           this.tokens.push({
16938             type: 'blockquote_start'
16939           });
16940     
16941           cap = cap[0].replace(/^ *> ?/gm, '');
16942     
16943           // Pass `top` to keep the current
16944           // "toplevel" state. This is exactly
16945           // how markdown.pl works.
16946           this.token(cap, top, true);
16947     
16948           this.tokens.push({
16949             type: 'blockquote_end'
16950           });
16951     
16952           continue;
16953         }
16954     
16955         // list
16956         if (cap = this.rules.list.exec(src)) {
16957           src = src.substring(cap[0].length);
16958           bull = cap[2];
16959     
16960           this.tokens.push({
16961             type: 'list_start',
16962             ordered: bull.length > 1
16963           });
16964     
16965           // Get each top-level item.
16966           cap = cap[0].match(this.rules.item);
16967     
16968           next = false;
16969           l = cap.length;
16970           i = 0;
16971     
16972           for (; i < l; i++) {
16973             item = cap[i];
16974     
16975             // Remove the list item's bullet
16976             // so it is seen as the next token.
16977             space = item.length;
16978             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16979     
16980             // Outdent whatever the
16981             // list item contains. Hacky.
16982             if (~item.indexOf('\n ')) {
16983               space -= item.length;
16984               item = !this.options.pedantic
16985                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16986                 : item.replace(/^ {1,4}/gm, '');
16987             }
16988     
16989             // Determine whether the next list item belongs here.
16990             // Backpedal if it does not belong in this list.
16991             if (this.options.smartLists && i !== l - 1) {
16992               b = block.bullet.exec(cap[i + 1])[0];
16993               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16994                 src = cap.slice(i + 1).join('\n') + src;
16995                 i = l - 1;
16996               }
16997             }
16998     
16999             // Determine whether item is loose or not.
17000             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17001             // for discount behavior.
17002             loose = next || /\n\n(?!\s*$)/.test(item);
17003             if (i !== l - 1) {
17004               next = item.charAt(item.length - 1) === '\n';
17005               if (!loose) { loose = next; }
17006             }
17007     
17008             this.tokens.push({
17009               type: loose
17010                 ? 'loose_item_start'
17011                 : 'list_item_start'
17012             });
17013     
17014             // Recurse.
17015             this.token(item, false, bq);
17016     
17017             this.tokens.push({
17018               type: 'list_item_end'
17019             });
17020           }
17021     
17022           this.tokens.push({
17023             type: 'list_end'
17024           });
17025     
17026           continue;
17027         }
17028     
17029         // html
17030         if (cap = this.rules.html.exec(src)) {
17031           src = src.substring(cap[0].length);
17032           this.tokens.push({
17033             type: this.options.sanitize
17034               ? 'paragraph'
17035               : 'html',
17036             pre: !this.options.sanitizer
17037               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17038             text: cap[0]
17039           });
17040           continue;
17041         }
17042     
17043         // def
17044         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17045           src = src.substring(cap[0].length);
17046           this.tokens.links[cap[1].toLowerCase()] = {
17047             href: cap[2],
17048             title: cap[3]
17049           };
17050           continue;
17051         }
17052     
17053         // table (gfm)
17054         if (top && (cap = this.rules.table.exec(src))) {
17055           src = src.substring(cap[0].length);
17056     
17057           item = {
17058             type: 'table',
17059             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17060             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17061             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17062           };
17063     
17064           for (i = 0; i < item.align.length; i++) {
17065             if (/^ *-+: *$/.test(item.align[i])) {
17066               item.align[i] = 'right';
17067             } else if (/^ *:-+: *$/.test(item.align[i])) {
17068               item.align[i] = 'center';
17069             } else if (/^ *:-+ *$/.test(item.align[i])) {
17070               item.align[i] = 'left';
17071             } else {
17072               item.align[i] = null;
17073             }
17074           }
17075     
17076           for (i = 0; i < item.cells.length; i++) {
17077             item.cells[i] = item.cells[i]
17078               .replace(/^ *\| *| *\| *$/g, '')
17079               .split(/ *\| */);
17080           }
17081     
17082           this.tokens.push(item);
17083     
17084           continue;
17085         }
17086     
17087         // top-level paragraph
17088         if (top && (cap = this.rules.paragraph.exec(src))) {
17089           src = src.substring(cap[0].length);
17090           this.tokens.push({
17091             type: 'paragraph',
17092             text: cap[1].charAt(cap[1].length - 1) === '\n'
17093               ? cap[1].slice(0, -1)
17094               : cap[1]
17095           });
17096           continue;
17097         }
17098     
17099         // text
17100         if (cap = this.rules.text.exec(src)) {
17101           // Top-level should never reach here.
17102           src = src.substring(cap[0].length);
17103           this.tokens.push({
17104             type: 'text',
17105             text: cap[0]
17106           });
17107           continue;
17108         }
17109     
17110         if (src) {
17111           throw new
17112             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17113         }
17114       }
17115     
17116       return this.tokens;
17117     };
17118     
17119     /**
17120      * Inline-Level Grammar
17121      */
17122     
17123     var inline = {
17124       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17125       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17126       url: noop,
17127       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17128       link: /^!?\[(inside)\]\(href\)/,
17129       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17130       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17131       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17132       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17133       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17134       br: /^ {2,}\n(?!\s*$)/,
17135       del: noop,
17136       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17137     };
17138     
17139     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17140     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17141     
17142     inline.link = replace(inline.link)
17143       ('inside', inline._inside)
17144       ('href', inline._href)
17145       ();
17146     
17147     inline.reflink = replace(inline.reflink)
17148       ('inside', inline._inside)
17149       ();
17150     
17151     /**
17152      * Normal Inline Grammar
17153      */
17154     
17155     inline.normal = merge({}, inline);
17156     
17157     /**
17158      * Pedantic Inline Grammar
17159      */
17160     
17161     inline.pedantic = merge({}, inline.normal, {
17162       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17163       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17164     });
17165     
17166     /**
17167      * GFM Inline Grammar
17168      */
17169     
17170     inline.gfm = merge({}, inline.normal, {
17171       escape: replace(inline.escape)('])', '~|])')(),
17172       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17173       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17174       text: replace(inline.text)
17175         (']|', '~]|')
17176         ('|', '|https?://|')
17177         ()
17178     });
17179     
17180     /**
17181      * GFM + Line Breaks Inline Grammar
17182      */
17183     
17184     inline.breaks = merge({}, inline.gfm, {
17185       br: replace(inline.br)('{2,}', '*')(),
17186       text: replace(inline.gfm.text)('{2,}', '*')()
17187     });
17188     
17189     /**
17190      * Inline Lexer & Compiler
17191      */
17192     
17193     function InlineLexer(links, options) {
17194       this.options = options || marked.defaults;
17195       this.links = links;
17196       this.rules = inline.normal;
17197       this.renderer = this.options.renderer || new Renderer;
17198       this.renderer.options = this.options;
17199     
17200       if (!this.links) {
17201         throw new
17202           Error('Tokens array requires a `links` property.');
17203       }
17204     
17205       if (this.options.gfm) {
17206         if (this.options.breaks) {
17207           this.rules = inline.breaks;
17208         } else {
17209           this.rules = inline.gfm;
17210         }
17211       } else if (this.options.pedantic) {
17212         this.rules = inline.pedantic;
17213       }
17214     }
17215     
17216     /**
17217      * Expose Inline Rules
17218      */
17219     
17220     InlineLexer.rules = inline;
17221     
17222     /**
17223      * Static Lexing/Compiling Method
17224      */
17225     
17226     InlineLexer.output = function(src, links, options) {
17227       var inline = new InlineLexer(links, options);
17228       return inline.output(src);
17229     };
17230     
17231     /**
17232      * Lexing/Compiling
17233      */
17234     
17235     InlineLexer.prototype.output = function(src) {
17236       var out = ''
17237         , link
17238         , text
17239         , href
17240         , cap;
17241     
17242       while (src) {
17243         // escape
17244         if (cap = this.rules.escape.exec(src)) {
17245           src = src.substring(cap[0].length);
17246           out += cap[1];
17247           continue;
17248         }
17249     
17250         // autolink
17251         if (cap = this.rules.autolink.exec(src)) {
17252           src = src.substring(cap[0].length);
17253           if (cap[2] === '@') {
17254             text = cap[1].charAt(6) === ':'
17255               ? this.mangle(cap[1].substring(7))
17256               : this.mangle(cap[1]);
17257             href = this.mangle('mailto:') + text;
17258           } else {
17259             text = escape(cap[1]);
17260             href = text;
17261           }
17262           out += this.renderer.link(href, null, text);
17263           continue;
17264         }
17265     
17266         // url (gfm)
17267         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17268           src = src.substring(cap[0].length);
17269           text = escape(cap[1]);
17270           href = text;
17271           out += this.renderer.link(href, null, text);
17272           continue;
17273         }
17274     
17275         // tag
17276         if (cap = this.rules.tag.exec(src)) {
17277           if (!this.inLink && /^<a /i.test(cap[0])) {
17278             this.inLink = true;
17279           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17280             this.inLink = false;
17281           }
17282           src = src.substring(cap[0].length);
17283           out += this.options.sanitize
17284             ? this.options.sanitizer
17285               ? this.options.sanitizer(cap[0])
17286               : escape(cap[0])
17287             : cap[0];
17288           continue;
17289         }
17290     
17291         // link
17292         if (cap = this.rules.link.exec(src)) {
17293           src = src.substring(cap[0].length);
17294           this.inLink = true;
17295           out += this.outputLink(cap, {
17296             href: cap[2],
17297             title: cap[3]
17298           });
17299           this.inLink = false;
17300           continue;
17301         }
17302     
17303         // reflink, nolink
17304         if ((cap = this.rules.reflink.exec(src))
17305             || (cap = this.rules.nolink.exec(src))) {
17306           src = src.substring(cap[0].length);
17307           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17308           link = this.links[link.toLowerCase()];
17309           if (!link || !link.href) {
17310             out += cap[0].charAt(0);
17311             src = cap[0].substring(1) + src;
17312             continue;
17313           }
17314           this.inLink = true;
17315           out += this.outputLink(cap, link);
17316           this.inLink = false;
17317           continue;
17318         }
17319     
17320         // strong
17321         if (cap = this.rules.strong.exec(src)) {
17322           src = src.substring(cap[0].length);
17323           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17324           continue;
17325         }
17326     
17327         // em
17328         if (cap = this.rules.em.exec(src)) {
17329           src = src.substring(cap[0].length);
17330           out += this.renderer.em(this.output(cap[2] || cap[1]));
17331           continue;
17332         }
17333     
17334         // code
17335         if (cap = this.rules.code.exec(src)) {
17336           src = src.substring(cap[0].length);
17337           out += this.renderer.codespan(escape(cap[2], true));
17338           continue;
17339         }
17340     
17341         // br
17342         if (cap = this.rules.br.exec(src)) {
17343           src = src.substring(cap[0].length);
17344           out += this.renderer.br();
17345           continue;
17346         }
17347     
17348         // del (gfm)
17349         if (cap = this.rules.del.exec(src)) {
17350           src = src.substring(cap[0].length);
17351           out += this.renderer.del(this.output(cap[1]));
17352           continue;
17353         }
17354     
17355         // text
17356         if (cap = this.rules.text.exec(src)) {
17357           src = src.substring(cap[0].length);
17358           out += this.renderer.text(escape(this.smartypants(cap[0])));
17359           continue;
17360         }
17361     
17362         if (src) {
17363           throw new
17364             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17365         }
17366       }
17367     
17368       return out;
17369     };
17370     
17371     /**
17372      * Compile Link
17373      */
17374     
17375     InlineLexer.prototype.outputLink = function(cap, link) {
17376       var href = escape(link.href)
17377         , title = link.title ? escape(link.title) : null;
17378     
17379       return cap[0].charAt(0) !== '!'
17380         ? this.renderer.link(href, title, this.output(cap[1]))
17381         : this.renderer.image(href, title, escape(cap[1]));
17382     };
17383     
17384     /**
17385      * Smartypants Transformations
17386      */
17387     
17388     InlineLexer.prototype.smartypants = function(text) {
17389       if (!this.options.smartypants)  { return text; }
17390       return text
17391         // em-dashes
17392         .replace(/---/g, '\u2014')
17393         // en-dashes
17394         .replace(/--/g, '\u2013')
17395         // opening singles
17396         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17397         // closing singles & apostrophes
17398         .replace(/'/g, '\u2019')
17399         // opening doubles
17400         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17401         // closing doubles
17402         .replace(/"/g, '\u201d')
17403         // ellipses
17404         .replace(/\.{3}/g, '\u2026');
17405     };
17406     
17407     /**
17408      * Mangle Links
17409      */
17410     
17411     InlineLexer.prototype.mangle = function(text) {
17412       if (!this.options.mangle) { return text; }
17413       var out = ''
17414         , l = text.length
17415         , i = 0
17416         , ch;
17417     
17418       for (; i < l; i++) {
17419         ch = text.charCodeAt(i);
17420         if (Math.random() > 0.5) {
17421           ch = 'x' + ch.toString(16);
17422         }
17423         out += '&#' + ch + ';';
17424       }
17425     
17426       return out;
17427     };
17428     
17429     /**
17430      * Renderer
17431      */
17432     
17433     function Renderer(options) {
17434       this.options = options || {};
17435     }
17436     
17437     Renderer.prototype.code = function(code, lang, escaped) {
17438       if (this.options.highlight) {
17439         var out = this.options.highlight(code, lang);
17440         if (out != null && out !== code) {
17441           escaped = true;
17442           code = out;
17443         }
17444       } else {
17445             // hack!!! - it's already escapeD?
17446             escaped = true;
17447       }
17448     
17449       if (!lang) {
17450         return '<pre><code>'
17451           + (escaped ? code : escape(code, true))
17452           + '\n</code></pre>';
17453       }
17454     
17455       return '<pre><code class="'
17456         + this.options.langPrefix
17457         + escape(lang, true)
17458         + '">'
17459         + (escaped ? code : escape(code, true))
17460         + '\n</code></pre>\n';
17461     };
17462     
17463     Renderer.prototype.blockquote = function(quote) {
17464       return '<blockquote>\n' + quote + '</blockquote>\n';
17465     };
17466     
17467     Renderer.prototype.html = function(html) {
17468       return html;
17469     };
17470     
17471     Renderer.prototype.heading = function(text, level, raw) {
17472       return '<h'
17473         + level
17474         + ' id="'
17475         + this.options.headerPrefix
17476         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17477         + '">'
17478         + text
17479         + '</h'
17480         + level
17481         + '>\n';
17482     };
17483     
17484     Renderer.prototype.hr = function() {
17485       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17486     };
17487     
17488     Renderer.prototype.list = function(body, ordered) {
17489       var type = ordered ? 'ol' : 'ul';
17490       return '<' + type + '>\n' + body + '</' + type + '>\n';
17491     };
17492     
17493     Renderer.prototype.listitem = function(text) {
17494       return '<li>' + text + '</li>\n';
17495     };
17496     
17497     Renderer.prototype.paragraph = function(text) {
17498       return '<p>' + text + '</p>\n';
17499     };
17500     
17501     Renderer.prototype.table = function(header, body) {
17502       return '<table class="table table-striped">\n'
17503         + '<thead>\n'
17504         + header
17505         + '</thead>\n'
17506         + '<tbody>\n'
17507         + body
17508         + '</tbody>\n'
17509         + '</table>\n';
17510     };
17511     
17512     Renderer.prototype.tablerow = function(content) {
17513       return '<tr>\n' + content + '</tr>\n';
17514     };
17515     
17516     Renderer.prototype.tablecell = function(content, flags) {
17517       var type = flags.header ? 'th' : 'td';
17518       var tag = flags.align
17519         ? '<' + type + ' style="text-align:' + flags.align + '">'
17520         : '<' + type + '>';
17521       return tag + content + '</' + type + '>\n';
17522     };
17523     
17524     // span level renderer
17525     Renderer.prototype.strong = function(text) {
17526       return '<strong>' + text + '</strong>';
17527     };
17528     
17529     Renderer.prototype.em = function(text) {
17530       return '<em>' + text + '</em>';
17531     };
17532     
17533     Renderer.prototype.codespan = function(text) {
17534       return '<code>' + text + '</code>';
17535     };
17536     
17537     Renderer.prototype.br = function() {
17538       return this.options.xhtml ? '<br/>' : '<br>';
17539     };
17540     
17541     Renderer.prototype.del = function(text) {
17542       return '<del>' + text + '</del>';
17543     };
17544     
17545     Renderer.prototype.link = function(href, title, text) {
17546       if (this.options.sanitize) {
17547         try {
17548           var prot = decodeURIComponent(unescape(href))
17549             .replace(/[^\w:]/g, '')
17550             .toLowerCase();
17551         } catch (e) {
17552           return '';
17553         }
17554         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17555           return '';
17556         }
17557       }
17558       var out = '<a href="' + href + '"';
17559       if (title) {
17560         out += ' title="' + title + '"';
17561       }
17562       out += '>' + text + '</a>';
17563       return out;
17564     };
17565     
17566     Renderer.prototype.image = function(href, title, text) {
17567       var out = '<img src="' + href + '" alt="' + text + '"';
17568       if (title) {
17569         out += ' title="' + title + '"';
17570       }
17571       out += this.options.xhtml ? '/>' : '>';
17572       return out;
17573     };
17574     
17575     Renderer.prototype.text = function(text) {
17576       return text;
17577     };
17578     
17579     /**
17580      * Parsing & Compiling
17581      */
17582     
17583     function Parser(options) {
17584       this.tokens = [];
17585       this.token = null;
17586       this.options = options || marked.defaults;
17587       this.options.renderer = this.options.renderer || new Renderer;
17588       this.renderer = this.options.renderer;
17589       this.renderer.options = this.options;
17590     }
17591     
17592     /**
17593      * Static Parse Method
17594      */
17595     
17596     Parser.parse = function(src, options, renderer) {
17597       var parser = new Parser(options, renderer);
17598       return parser.parse(src);
17599     };
17600     
17601     /**
17602      * Parse Loop
17603      */
17604     
17605     Parser.prototype.parse = function(src) {
17606       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17607       this.tokens = src.reverse();
17608     
17609       var out = '';
17610       while (this.next()) {
17611         out += this.tok();
17612       }
17613     
17614       return out;
17615     };
17616     
17617     /**
17618      * Next Token
17619      */
17620     
17621     Parser.prototype.next = function() {
17622       return this.token = this.tokens.pop();
17623     };
17624     
17625     /**
17626      * Preview Next Token
17627      */
17628     
17629     Parser.prototype.peek = function() {
17630       return this.tokens[this.tokens.length - 1] || 0;
17631     };
17632     
17633     /**
17634      * Parse Text Tokens
17635      */
17636     
17637     Parser.prototype.parseText = function() {
17638       var body = this.token.text;
17639     
17640       while (this.peek().type === 'text') {
17641         body += '\n' + this.next().text;
17642       }
17643     
17644       return this.inline.output(body);
17645     };
17646     
17647     /**
17648      * Parse Current Token
17649      */
17650     
17651     Parser.prototype.tok = function() {
17652       switch (this.token.type) {
17653         case 'space': {
17654           return '';
17655         }
17656         case 'hr': {
17657           return this.renderer.hr();
17658         }
17659         case 'heading': {
17660           return this.renderer.heading(
17661             this.inline.output(this.token.text),
17662             this.token.depth,
17663             this.token.text);
17664         }
17665         case 'code': {
17666           return this.renderer.code(this.token.text,
17667             this.token.lang,
17668             this.token.escaped);
17669         }
17670         case 'table': {
17671           var header = ''
17672             , body = ''
17673             , i
17674             , row
17675             , cell
17676             , flags
17677             , j;
17678     
17679           // header
17680           cell = '';
17681           for (i = 0; i < this.token.header.length; i++) {
17682             flags = { header: true, align: this.token.align[i] };
17683             cell += this.renderer.tablecell(
17684               this.inline.output(this.token.header[i]),
17685               { header: true, align: this.token.align[i] }
17686             );
17687           }
17688           header += this.renderer.tablerow(cell);
17689     
17690           for (i = 0; i < this.token.cells.length; i++) {
17691             row = this.token.cells[i];
17692     
17693             cell = '';
17694             for (j = 0; j < row.length; j++) {
17695               cell += this.renderer.tablecell(
17696                 this.inline.output(row[j]),
17697                 { header: false, align: this.token.align[j] }
17698               );
17699             }
17700     
17701             body += this.renderer.tablerow(cell);
17702           }
17703           return this.renderer.table(header, body);
17704         }
17705         case 'blockquote_start': {
17706           var body = '';
17707     
17708           while (this.next().type !== 'blockquote_end') {
17709             body += this.tok();
17710           }
17711     
17712           return this.renderer.blockquote(body);
17713         }
17714         case 'list_start': {
17715           var body = ''
17716             , ordered = this.token.ordered;
17717     
17718           while (this.next().type !== 'list_end') {
17719             body += this.tok();
17720           }
17721     
17722           return this.renderer.list(body, ordered);
17723         }
17724         case 'list_item_start': {
17725           var body = '';
17726     
17727           while (this.next().type !== 'list_item_end') {
17728             body += this.token.type === 'text'
17729               ? this.parseText()
17730               : this.tok();
17731           }
17732     
17733           return this.renderer.listitem(body);
17734         }
17735         case 'loose_item_start': {
17736           var body = '';
17737     
17738           while (this.next().type !== 'list_item_end') {
17739             body += this.tok();
17740           }
17741     
17742           return this.renderer.listitem(body);
17743         }
17744         case 'html': {
17745           var html = !this.token.pre && !this.options.pedantic
17746             ? this.inline.output(this.token.text)
17747             : this.token.text;
17748           return this.renderer.html(html);
17749         }
17750         case 'paragraph': {
17751           return this.renderer.paragraph(this.inline.output(this.token.text));
17752         }
17753         case 'text': {
17754           return this.renderer.paragraph(this.parseText());
17755         }
17756       }
17757     };
17758     
17759     /**
17760      * Helpers
17761      */
17762     
17763     function escape(html, encode) {
17764       return html
17765         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17766         .replace(/</g, '&lt;')
17767         .replace(/>/g, '&gt;')
17768         .replace(/"/g, '&quot;')
17769         .replace(/'/g, '&#39;');
17770     }
17771     
17772     function unescape(html) {
17773         // explicitly match decimal, hex, and named HTML entities 
17774       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17775         n = n.toLowerCase();
17776         if (n === 'colon') { return ':'; }
17777         if (n.charAt(0) === '#') {
17778           return n.charAt(1) === 'x'
17779             ? String.fromCharCode(parseInt(n.substring(2), 16))
17780             : String.fromCharCode(+n.substring(1));
17781         }
17782         return '';
17783       });
17784     }
17785     
17786     function replace(regex, opt) {
17787       regex = regex.source;
17788       opt = opt || '';
17789       return function self(name, val) {
17790         if (!name) { return new RegExp(regex, opt); }
17791         val = val.source || val;
17792         val = val.replace(/(^|[^\[])\^/g, '$1');
17793         regex = regex.replace(name, val);
17794         return self;
17795       };
17796     }
17797     
17798     function noop() {}
17799     noop.exec = noop;
17800     
17801     function merge(obj) {
17802       var i = 1
17803         , target
17804         , key;
17805     
17806       for (; i < arguments.length; i++) {
17807         target = arguments[i];
17808         for (key in target) {
17809           if (Object.prototype.hasOwnProperty.call(target, key)) {
17810             obj[key] = target[key];
17811           }
17812         }
17813       }
17814     
17815       return obj;
17816     }
17817     
17818     
17819     /**
17820      * Marked
17821      */
17822     
17823     function marked(src, opt, callback) {
17824       if (callback || typeof opt === 'function') {
17825         if (!callback) {
17826           callback = opt;
17827           opt = null;
17828         }
17829     
17830         opt = merge({}, marked.defaults, opt || {});
17831     
17832         var highlight = opt.highlight
17833           , tokens
17834           , pending
17835           , i = 0;
17836     
17837         try {
17838           tokens = Lexer.lex(src, opt)
17839         } catch (e) {
17840           return callback(e);
17841         }
17842     
17843         pending = tokens.length;
17844     
17845         var done = function(err) {
17846           if (err) {
17847             opt.highlight = highlight;
17848             return callback(err);
17849           }
17850     
17851           var out;
17852     
17853           try {
17854             out = Parser.parse(tokens, opt);
17855           } catch (e) {
17856             err = e;
17857           }
17858     
17859           opt.highlight = highlight;
17860     
17861           return err
17862             ? callback(err)
17863             : callback(null, out);
17864         };
17865     
17866         if (!highlight || highlight.length < 3) {
17867           return done();
17868         }
17869     
17870         delete opt.highlight;
17871     
17872         if (!pending) { return done(); }
17873     
17874         for (; i < tokens.length; i++) {
17875           (function(token) {
17876             if (token.type !== 'code') {
17877               return --pending || done();
17878             }
17879             return highlight(token.text, token.lang, function(err, code) {
17880               if (err) { return done(err); }
17881               if (code == null || code === token.text) {
17882                 return --pending || done();
17883               }
17884               token.text = code;
17885               token.escaped = true;
17886               --pending || done();
17887             });
17888           })(tokens[i]);
17889         }
17890     
17891         return;
17892       }
17893       try {
17894         if (opt) { opt = merge({}, marked.defaults, opt); }
17895         return Parser.parse(Lexer.lex(src, opt), opt);
17896       } catch (e) {
17897         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17898         if ((opt || marked.defaults).silent) {
17899           return '<p>An error occured:</p><pre>'
17900             + escape(e.message + '', true)
17901             + '</pre>';
17902         }
17903         throw e;
17904       }
17905     }
17906     
17907     /**
17908      * Options
17909      */
17910     
17911     marked.options =
17912     marked.setOptions = function(opt) {
17913       merge(marked.defaults, opt);
17914       return marked;
17915     };
17916     
17917     marked.defaults = {
17918       gfm: true,
17919       tables: true,
17920       breaks: false,
17921       pedantic: false,
17922       sanitize: false,
17923       sanitizer: null,
17924       mangle: true,
17925       smartLists: false,
17926       silent: false,
17927       highlight: null,
17928       langPrefix: 'lang-',
17929       smartypants: false,
17930       headerPrefix: '',
17931       renderer: new Renderer,
17932       xhtml: false
17933     };
17934     
17935     /**
17936      * Expose
17937      */
17938     
17939     marked.Parser = Parser;
17940     marked.parser = Parser.parse;
17941     
17942     marked.Renderer = Renderer;
17943     
17944     marked.Lexer = Lexer;
17945     marked.lexer = Lexer.lex;
17946     
17947     marked.InlineLexer = InlineLexer;
17948     marked.inlineLexer = InlineLexer.output;
17949     
17950     marked.parse = marked;
17951     
17952     Roo.Markdown.marked = marked;
17953
17954 })();/*
17955  * Based on:
17956  * Ext JS Library 1.1.1
17957  * Copyright(c) 2006-2007, Ext JS, LLC.
17958  *
17959  * Originally Released Under LGPL - original licence link has changed is not relivant.
17960  *
17961  * Fork - LGPL
17962  * <script type="text/javascript">
17963  */
17964
17965
17966
17967 /*
17968  * These classes are derivatives of the similarly named classes in the YUI Library.
17969  * The original license:
17970  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17971  * Code licensed under the BSD License:
17972  * http://developer.yahoo.net/yui/license.txt
17973  */
17974
17975 (function() {
17976
17977 var Event=Roo.EventManager;
17978 var Dom=Roo.lib.Dom;
17979
17980 /**
17981  * @class Roo.dd.DragDrop
17982  * @extends Roo.util.Observable
17983  * Defines the interface and base operation of items that that can be
17984  * dragged or can be drop targets.  It was designed to be extended, overriding
17985  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17986  * Up to three html elements can be associated with a DragDrop instance:
17987  * <ul>
17988  * <li>linked element: the element that is passed into the constructor.
17989  * This is the element which defines the boundaries for interaction with
17990  * other DragDrop objects.</li>
17991  * <li>handle element(s): The drag operation only occurs if the element that
17992  * was clicked matches a handle element.  By default this is the linked
17993  * element, but there are times that you will want only a portion of the
17994  * linked element to initiate the drag operation, and the setHandleElId()
17995  * method provides a way to define this.</li>
17996  * <li>drag element: this represents the element that would be moved along
17997  * with the cursor during a drag operation.  By default, this is the linked
17998  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17999  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18000  * </li>
18001  * </ul>
18002  * This class should not be instantiated until the onload event to ensure that
18003  * the associated elements are available.
18004  * The following would define a DragDrop obj that would interact with any
18005  * other DragDrop obj in the "group1" group:
18006  * <pre>
18007  *  dd = new Roo.dd.DragDrop("div1", "group1");
18008  * </pre>
18009  * Since none of the event handlers have been implemented, nothing would
18010  * actually happen if you were to run the code above.  Normally you would
18011  * override this class or one of the default implementations, but you can
18012  * also override the methods you want on an instance of the class...
18013  * <pre>
18014  *  dd.onDragDrop = function(e, id) {
18015  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18016  *  }
18017  * </pre>
18018  * @constructor
18019  * @param {String} id of the element that is linked to this instance
18020  * @param {String} sGroup the group of related DragDrop objects
18021  * @param {object} config an object containing configurable attributes
18022  *                Valid properties for DragDrop:
18023  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18024  */
18025 Roo.dd.DragDrop = function(id, sGroup, config) {
18026     if (id) {
18027         this.init(id, sGroup, config);
18028     }
18029     
18030 };
18031
18032 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18033
18034     /**
18035      * The id of the element associated with this object.  This is what we
18036      * refer to as the "linked element" because the size and position of
18037      * this element is used to determine when the drag and drop objects have
18038      * interacted.
18039      * @property id
18040      * @type String
18041      */
18042     id: null,
18043
18044     /**
18045      * Configuration attributes passed into the constructor
18046      * @property config
18047      * @type object
18048      */
18049     config: null,
18050
18051     /**
18052      * The id of the element that will be dragged.  By default this is same
18053      * as the linked element , but could be changed to another element. Ex:
18054      * Roo.dd.DDProxy
18055      * @property dragElId
18056      * @type String
18057      * @private
18058      */
18059     dragElId: null,
18060
18061     /**
18062      * the id of the element that initiates the drag operation.  By default
18063      * this is the linked element, but could be changed to be a child of this
18064      * element.  This lets us do things like only starting the drag when the
18065      * header element within the linked html element is clicked.
18066      * @property handleElId
18067      * @type String
18068      * @private
18069      */
18070     handleElId: null,
18071
18072     /**
18073      * An associative array of HTML tags that will be ignored if clicked.
18074      * @property invalidHandleTypes
18075      * @type {string: string}
18076      */
18077     invalidHandleTypes: null,
18078
18079     /**
18080      * An associative array of ids for elements that will be ignored if clicked
18081      * @property invalidHandleIds
18082      * @type {string: string}
18083      */
18084     invalidHandleIds: null,
18085
18086     /**
18087      * An indexted array of css class names for elements that will be ignored
18088      * if clicked.
18089      * @property invalidHandleClasses
18090      * @type string[]
18091      */
18092     invalidHandleClasses: null,
18093
18094     /**
18095      * The linked element's absolute X position at the time the drag was
18096      * started
18097      * @property startPageX
18098      * @type int
18099      * @private
18100      */
18101     startPageX: 0,
18102
18103     /**
18104      * The linked element's absolute X position at the time the drag was
18105      * started
18106      * @property startPageY
18107      * @type int
18108      * @private
18109      */
18110     startPageY: 0,
18111
18112     /**
18113      * The group defines a logical collection of DragDrop objects that are
18114      * related.  Instances only get events when interacting with other
18115      * DragDrop object in the same group.  This lets us define multiple
18116      * groups using a single DragDrop subclass if we want.
18117      * @property groups
18118      * @type {string: string}
18119      */
18120     groups: null,
18121
18122     /**
18123      * Individual drag/drop instances can be locked.  This will prevent
18124      * onmousedown start drag.
18125      * @property locked
18126      * @type boolean
18127      * @private
18128      */
18129     locked: false,
18130
18131     /**
18132      * Lock this instance
18133      * @method lock
18134      */
18135     lock: function() { this.locked = true; },
18136
18137     /**
18138      * Unlock this instace
18139      * @method unlock
18140      */
18141     unlock: function() { this.locked = false; },
18142
18143     /**
18144      * By default, all insances can be a drop target.  This can be disabled by
18145      * setting isTarget to false.
18146      * @method isTarget
18147      * @type boolean
18148      */
18149     isTarget: true,
18150
18151     /**
18152      * The padding configured for this drag and drop object for calculating
18153      * the drop zone intersection with this object.
18154      * @method padding
18155      * @type int[]
18156      */
18157     padding: null,
18158
18159     /**
18160      * Cached reference to the linked element
18161      * @property _domRef
18162      * @private
18163      */
18164     _domRef: null,
18165
18166     /**
18167      * Internal typeof flag
18168      * @property __ygDragDrop
18169      * @private
18170      */
18171     __ygDragDrop: true,
18172
18173     /**
18174      * Set to true when horizontal contraints are applied
18175      * @property constrainX
18176      * @type boolean
18177      * @private
18178      */
18179     constrainX: false,
18180
18181     /**
18182      * Set to true when vertical contraints are applied
18183      * @property constrainY
18184      * @type boolean
18185      * @private
18186      */
18187     constrainY: false,
18188
18189     /**
18190      * The left constraint
18191      * @property minX
18192      * @type int
18193      * @private
18194      */
18195     minX: 0,
18196
18197     /**
18198      * The right constraint
18199      * @property maxX
18200      * @type int
18201      * @private
18202      */
18203     maxX: 0,
18204
18205     /**
18206      * The up constraint
18207      * @property minY
18208      * @type int
18209      * @type int
18210      * @private
18211      */
18212     minY: 0,
18213
18214     /**
18215      * The down constraint
18216      * @property maxY
18217      * @type int
18218      * @private
18219      */
18220     maxY: 0,
18221
18222     /**
18223      * Maintain offsets when we resetconstraints.  Set to true when you want
18224      * the position of the element relative to its parent to stay the same
18225      * when the page changes
18226      *
18227      * @property maintainOffset
18228      * @type boolean
18229      */
18230     maintainOffset: false,
18231
18232     /**
18233      * Array of pixel locations the element will snap to if we specified a
18234      * horizontal graduation/interval.  This array is generated automatically
18235      * when you define a tick interval.
18236      * @property xTicks
18237      * @type int[]
18238      */
18239     xTicks: null,
18240
18241     /**
18242      * Array of pixel locations the element will snap to if we specified a
18243      * vertical graduation/interval.  This array is generated automatically
18244      * when you define a tick interval.
18245      * @property yTicks
18246      * @type int[]
18247      */
18248     yTicks: null,
18249
18250     /**
18251      * By default the drag and drop instance will only respond to the primary
18252      * button click (left button for a right-handed mouse).  Set to true to
18253      * allow drag and drop to start with any mouse click that is propogated
18254      * by the browser
18255      * @property primaryButtonOnly
18256      * @type boolean
18257      */
18258     primaryButtonOnly: true,
18259
18260     /**
18261      * The availabe property is false until the linked dom element is accessible.
18262      * @property available
18263      * @type boolean
18264      */
18265     available: false,
18266
18267     /**
18268      * By default, drags can only be initiated if the mousedown occurs in the
18269      * region the linked element is.  This is done in part to work around a
18270      * bug in some browsers that mis-report the mousedown if the previous
18271      * mouseup happened outside of the window.  This property is set to true
18272      * if outer handles are defined.
18273      *
18274      * @property hasOuterHandles
18275      * @type boolean
18276      * @default false
18277      */
18278     hasOuterHandles: false,
18279
18280     /**
18281      * Code that executes immediately before the startDrag event
18282      * @method b4StartDrag
18283      * @private
18284      */
18285     b4StartDrag: function(x, y) { },
18286
18287     /**
18288      * Abstract method called after a drag/drop object is clicked
18289      * and the drag or mousedown time thresholds have beeen met.
18290      * @method startDrag
18291      * @param {int} X click location
18292      * @param {int} Y click location
18293      */
18294     startDrag: function(x, y) { /* override this */ },
18295
18296     /**
18297      * Code that executes immediately before the onDrag event
18298      * @method b4Drag
18299      * @private
18300      */
18301     b4Drag: function(e) { },
18302
18303     /**
18304      * Abstract method called during the onMouseMove event while dragging an
18305      * object.
18306      * @method onDrag
18307      * @param {Event} e the mousemove event
18308      */
18309     onDrag: function(e) { /* override this */ },
18310
18311     /**
18312      * Abstract method called when this element fist begins hovering over
18313      * another DragDrop obj
18314      * @method onDragEnter
18315      * @param {Event} e the mousemove event
18316      * @param {String|DragDrop[]} id In POINT mode, the element
18317      * id this is hovering over.  In INTERSECT mode, an array of one or more
18318      * dragdrop items being hovered over.
18319      */
18320     onDragEnter: function(e, id) { /* override this */ },
18321
18322     /**
18323      * Code that executes immediately before the onDragOver event
18324      * @method b4DragOver
18325      * @private
18326      */
18327     b4DragOver: function(e) { },
18328
18329     /**
18330      * Abstract method called when this element is hovering over another
18331      * DragDrop obj
18332      * @method onDragOver
18333      * @param {Event} e the mousemove event
18334      * @param {String|DragDrop[]} id In POINT mode, the element
18335      * id this is hovering over.  In INTERSECT mode, an array of dd items
18336      * being hovered over.
18337      */
18338     onDragOver: function(e, id) { /* override this */ },
18339
18340     /**
18341      * Code that executes immediately before the onDragOut event
18342      * @method b4DragOut
18343      * @private
18344      */
18345     b4DragOut: function(e) { },
18346
18347     /**
18348      * Abstract method called when we are no longer hovering over an element
18349      * @method onDragOut
18350      * @param {Event} e the mousemove event
18351      * @param {String|DragDrop[]} id In POINT mode, the element
18352      * id this was hovering over.  In INTERSECT mode, an array of dd items
18353      * that the mouse is no longer over.
18354      */
18355     onDragOut: function(e, id) { /* override this */ },
18356
18357     /**
18358      * Code that executes immediately before the onDragDrop event
18359      * @method b4DragDrop
18360      * @private
18361      */
18362     b4DragDrop: function(e) { },
18363
18364     /**
18365      * Abstract method called when this item is dropped on another DragDrop
18366      * obj
18367      * @method onDragDrop
18368      * @param {Event} e the mouseup event
18369      * @param {String|DragDrop[]} id In POINT mode, the element
18370      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18371      * was dropped on.
18372      */
18373     onDragDrop: function(e, id) { /* override this */ },
18374
18375     /**
18376      * Abstract method called when this item is dropped on an area with no
18377      * drop target
18378      * @method onInvalidDrop
18379      * @param {Event} e the mouseup event
18380      */
18381     onInvalidDrop: function(e) { /* override this */ },
18382
18383     /**
18384      * Code that executes immediately before the endDrag event
18385      * @method b4EndDrag
18386      * @private
18387      */
18388     b4EndDrag: function(e) { },
18389
18390     /**
18391      * Fired when we are done dragging the object
18392      * @method endDrag
18393      * @param {Event} e the mouseup event
18394      */
18395     endDrag: function(e) { /* override this */ },
18396
18397     /**
18398      * Code executed immediately before the onMouseDown event
18399      * @method b4MouseDown
18400      * @param {Event} e the mousedown event
18401      * @private
18402      */
18403     b4MouseDown: function(e) {  },
18404
18405     /**
18406      * Event handler that fires when a drag/drop obj gets a mousedown
18407      * @method onMouseDown
18408      * @param {Event} e the mousedown event
18409      */
18410     onMouseDown: function(e) { /* override this */ },
18411
18412     /**
18413      * Event handler that fires when a drag/drop obj gets a mouseup
18414      * @method onMouseUp
18415      * @param {Event} e the mouseup event
18416      */
18417     onMouseUp: function(e) { /* override this */ },
18418
18419     /**
18420      * Override the onAvailable method to do what is needed after the initial
18421      * position was determined.
18422      * @method onAvailable
18423      */
18424     onAvailable: function () {
18425     },
18426
18427     /*
18428      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18429      * @type Object
18430      */
18431     defaultPadding : {left:0, right:0, top:0, bottom:0},
18432
18433     /*
18434      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18435  *
18436  * Usage:
18437  <pre><code>
18438  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18439                 { dragElId: "existingProxyDiv" });
18440  dd.startDrag = function(){
18441      this.constrainTo("parent-id");
18442  };
18443  </code></pre>
18444  * Or you can initalize it using the {@link Roo.Element} object:
18445  <pre><code>
18446  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18447      startDrag : function(){
18448          this.constrainTo("parent-id");
18449      }
18450  });
18451  </code></pre>
18452      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18453      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18454      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18455      * an object containing the sides to pad. For example: {right:10, bottom:10}
18456      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18457      */
18458     constrainTo : function(constrainTo, pad, inContent){
18459         if(typeof pad == "number"){
18460             pad = {left: pad, right:pad, top:pad, bottom:pad};
18461         }
18462         pad = pad || this.defaultPadding;
18463         var b = Roo.get(this.getEl()).getBox();
18464         var ce = Roo.get(constrainTo);
18465         var s = ce.getScroll();
18466         var c, cd = ce.dom;
18467         if(cd == document.body){
18468             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18469         }else{
18470             xy = ce.getXY();
18471             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18472         }
18473
18474
18475         var topSpace = b.y - c.y;
18476         var leftSpace = b.x - c.x;
18477
18478         this.resetConstraints();
18479         this.setXConstraint(leftSpace - (pad.left||0), // left
18480                 c.width - leftSpace - b.width - (pad.right||0) //right
18481         );
18482         this.setYConstraint(topSpace - (pad.top||0), //top
18483                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18484         );
18485     },
18486
18487     /**
18488      * Returns a reference to the linked element
18489      * @method getEl
18490      * @return {HTMLElement} the html element
18491      */
18492     getEl: function() {
18493         if (!this._domRef) {
18494             this._domRef = Roo.getDom(this.id);
18495         }
18496
18497         return this._domRef;
18498     },
18499
18500     /**
18501      * Returns a reference to the actual element to drag.  By default this is
18502      * the same as the html element, but it can be assigned to another
18503      * element. An example of this can be found in Roo.dd.DDProxy
18504      * @method getDragEl
18505      * @return {HTMLElement} the html element
18506      */
18507     getDragEl: function() {
18508         return Roo.getDom(this.dragElId);
18509     },
18510
18511     /**
18512      * Sets up the DragDrop object.  Must be called in the constructor of any
18513      * Roo.dd.DragDrop subclass
18514      * @method init
18515      * @param id the id of the linked element
18516      * @param {String} sGroup the group of related items
18517      * @param {object} config configuration attributes
18518      */
18519     init: function(id, sGroup, config) {
18520         this.initTarget(id, sGroup, config);
18521         if (!Roo.isTouch) {
18522             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18523         }
18524         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18525         // Event.on(this.id, "selectstart", Event.preventDefault);
18526     },
18527
18528     /**
18529      * Initializes Targeting functionality only... the object does not
18530      * get a mousedown handler.
18531      * @method initTarget
18532      * @param id the id of the linked element
18533      * @param {String} sGroup the group of related items
18534      * @param {object} config configuration attributes
18535      */
18536     initTarget: function(id, sGroup, config) {
18537
18538         // configuration attributes
18539         this.config = config || {};
18540
18541         // create a local reference to the drag and drop manager
18542         this.DDM = Roo.dd.DDM;
18543         // initialize the groups array
18544         this.groups = {};
18545
18546         // assume that we have an element reference instead of an id if the
18547         // parameter is not a string
18548         if (typeof id !== "string") {
18549             id = Roo.id(id);
18550         }
18551
18552         // set the id
18553         this.id = id;
18554
18555         // add to an interaction group
18556         this.addToGroup((sGroup) ? sGroup : "default");
18557
18558         // We don't want to register this as the handle with the manager
18559         // so we just set the id rather than calling the setter.
18560         this.handleElId = id;
18561
18562         // the linked element is the element that gets dragged by default
18563         this.setDragElId(id);
18564
18565         // by default, clicked anchors will not start drag operations.
18566         this.invalidHandleTypes = { A: "A" };
18567         this.invalidHandleIds = {};
18568         this.invalidHandleClasses = [];
18569
18570         this.applyConfig();
18571
18572         this.handleOnAvailable();
18573     },
18574
18575     /**
18576      * Applies the configuration parameters that were passed into the constructor.
18577      * This is supposed to happen at each level through the inheritance chain.  So
18578      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18579      * DragDrop in order to get all of the parameters that are available in
18580      * each object.
18581      * @method applyConfig
18582      */
18583     applyConfig: function() {
18584
18585         // configurable properties:
18586         //    padding, isTarget, maintainOffset, primaryButtonOnly
18587         this.padding           = this.config.padding || [0, 0, 0, 0];
18588         this.isTarget          = (this.config.isTarget !== false);
18589         this.maintainOffset    = (this.config.maintainOffset);
18590         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18591
18592     },
18593
18594     /**
18595      * Executed when the linked element is available
18596      * @method handleOnAvailable
18597      * @private
18598      */
18599     handleOnAvailable: function() {
18600         this.available = true;
18601         this.resetConstraints();
18602         this.onAvailable();
18603     },
18604
18605      /**
18606      * Configures the padding for the target zone in px.  Effectively expands
18607      * (or reduces) the virtual object size for targeting calculations.
18608      * Supports css-style shorthand; if only one parameter is passed, all sides
18609      * will have that padding, and if only two are passed, the top and bottom
18610      * will have the first param, the left and right the second.
18611      * @method setPadding
18612      * @param {int} iTop    Top pad
18613      * @param {int} iRight  Right pad
18614      * @param {int} iBot    Bot pad
18615      * @param {int} iLeft   Left pad
18616      */
18617     setPadding: function(iTop, iRight, iBot, iLeft) {
18618         // this.padding = [iLeft, iRight, iTop, iBot];
18619         if (!iRight && 0 !== iRight) {
18620             this.padding = [iTop, iTop, iTop, iTop];
18621         } else if (!iBot && 0 !== iBot) {
18622             this.padding = [iTop, iRight, iTop, iRight];
18623         } else {
18624             this.padding = [iTop, iRight, iBot, iLeft];
18625         }
18626     },
18627
18628     /**
18629      * Stores the initial placement of the linked element.
18630      * @method setInitialPosition
18631      * @param {int} diffX   the X offset, default 0
18632      * @param {int} diffY   the Y offset, default 0
18633      */
18634     setInitPosition: function(diffX, diffY) {
18635         var el = this.getEl();
18636
18637         if (!this.DDM.verifyEl(el)) {
18638             return;
18639         }
18640
18641         var dx = diffX || 0;
18642         var dy = diffY || 0;
18643
18644         var p = Dom.getXY( el );
18645
18646         this.initPageX = p[0] - dx;
18647         this.initPageY = p[1] - dy;
18648
18649         this.lastPageX = p[0];
18650         this.lastPageY = p[1];
18651
18652
18653         this.setStartPosition(p);
18654     },
18655
18656     /**
18657      * Sets the start position of the element.  This is set when the obj
18658      * is initialized, the reset when a drag is started.
18659      * @method setStartPosition
18660      * @param pos current position (from previous lookup)
18661      * @private
18662      */
18663     setStartPosition: function(pos) {
18664         var p = pos || Dom.getXY( this.getEl() );
18665         this.deltaSetXY = null;
18666
18667         this.startPageX = p[0];
18668         this.startPageY = p[1];
18669     },
18670
18671     /**
18672      * Add this instance to a group of related drag/drop objects.  All
18673      * instances belong to at least one group, and can belong to as many
18674      * groups as needed.
18675      * @method addToGroup
18676      * @param sGroup {string} the name of the group
18677      */
18678     addToGroup: function(sGroup) {
18679         this.groups[sGroup] = true;
18680         this.DDM.regDragDrop(this, sGroup);
18681     },
18682
18683     /**
18684      * Remove's this instance from the supplied interaction group
18685      * @method removeFromGroup
18686      * @param {string}  sGroup  The group to drop
18687      */
18688     removeFromGroup: function(sGroup) {
18689         if (this.groups[sGroup]) {
18690             delete this.groups[sGroup];
18691         }
18692
18693         this.DDM.removeDDFromGroup(this, sGroup);
18694     },
18695
18696     /**
18697      * Allows you to specify that an element other than the linked element
18698      * will be moved with the cursor during a drag
18699      * @method setDragElId
18700      * @param id {string} the id of the element that will be used to initiate the drag
18701      */
18702     setDragElId: function(id) {
18703         this.dragElId = id;
18704     },
18705
18706     /**
18707      * Allows you to specify a child of the linked element that should be
18708      * used to initiate the drag operation.  An example of this would be if
18709      * you have a content div with text and links.  Clicking anywhere in the
18710      * content area would normally start the drag operation.  Use this method
18711      * to specify that an element inside of the content div is the element
18712      * that starts the drag operation.
18713      * @method setHandleElId
18714      * @param id {string} the id of the element that will be used to
18715      * initiate the drag.
18716      */
18717     setHandleElId: function(id) {
18718         if (typeof id !== "string") {
18719             id = Roo.id(id);
18720         }
18721         this.handleElId = id;
18722         this.DDM.regHandle(this.id, id);
18723     },
18724
18725     /**
18726      * Allows you to set an element outside of the linked element as a drag
18727      * handle
18728      * @method setOuterHandleElId
18729      * @param id the id of the element that will be used to initiate the drag
18730      */
18731     setOuterHandleElId: function(id) {
18732         if (typeof id !== "string") {
18733             id = Roo.id(id);
18734         }
18735         Event.on(id, "mousedown",
18736                 this.handleMouseDown, this);
18737         this.setHandleElId(id);
18738
18739         this.hasOuterHandles = true;
18740     },
18741
18742     /**
18743      * Remove all drag and drop hooks for this element
18744      * @method unreg
18745      */
18746     unreg: function() {
18747         Event.un(this.id, "mousedown",
18748                 this.handleMouseDown);
18749         Event.un(this.id, "touchstart",
18750                 this.handleMouseDown);
18751         this._domRef = null;
18752         this.DDM._remove(this);
18753     },
18754
18755     destroy : function(){
18756         this.unreg();
18757     },
18758
18759     /**
18760      * Returns true if this instance is locked, or the drag drop mgr is locked
18761      * (meaning that all drag/drop is disabled on the page.)
18762      * @method isLocked
18763      * @return {boolean} true if this obj or all drag/drop is locked, else
18764      * false
18765      */
18766     isLocked: function() {
18767         return (this.DDM.isLocked() || this.locked);
18768     },
18769
18770     /**
18771      * Fired when this object is clicked
18772      * @method handleMouseDown
18773      * @param {Event} e
18774      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18775      * @private
18776      */
18777     handleMouseDown: function(e, oDD){
18778      
18779         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18780             //Roo.log('not touch/ button !=0');
18781             return;
18782         }
18783         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18784             return; // double touch..
18785         }
18786         
18787
18788         if (this.isLocked()) {
18789             //Roo.log('locked');
18790             return;
18791         }
18792
18793         this.DDM.refreshCache(this.groups);
18794 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18795         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18796         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18797             //Roo.log('no outer handes or not over target');
18798                 // do nothing.
18799         } else {
18800 //            Roo.log('check validator');
18801             if (this.clickValidator(e)) {
18802 //                Roo.log('validate success');
18803                 // set the initial element position
18804                 this.setStartPosition();
18805
18806
18807                 this.b4MouseDown(e);
18808                 this.onMouseDown(e);
18809
18810                 this.DDM.handleMouseDown(e, this);
18811
18812                 this.DDM.stopEvent(e);
18813             } else {
18814
18815
18816             }
18817         }
18818     },
18819
18820     clickValidator: function(e) {
18821         var target = e.getTarget();
18822         return ( this.isValidHandleChild(target) &&
18823                     (this.id == this.handleElId ||
18824                         this.DDM.handleWasClicked(target, this.id)) );
18825     },
18826
18827     /**
18828      * Allows you to specify a tag name that should not start a drag operation
18829      * when clicked.  This is designed to facilitate embedding links within a
18830      * drag handle that do something other than start the drag.
18831      * @method addInvalidHandleType
18832      * @param {string} tagName the type of element to exclude
18833      */
18834     addInvalidHandleType: function(tagName) {
18835         var type = tagName.toUpperCase();
18836         this.invalidHandleTypes[type] = type;
18837     },
18838
18839     /**
18840      * Lets you to specify an element id for a child of a drag handle
18841      * that should not initiate a drag
18842      * @method addInvalidHandleId
18843      * @param {string} id the element id of the element you wish to ignore
18844      */
18845     addInvalidHandleId: function(id) {
18846         if (typeof id !== "string") {
18847             id = Roo.id(id);
18848         }
18849         this.invalidHandleIds[id] = id;
18850     },
18851
18852     /**
18853      * Lets you specify a css class of elements that will not initiate a drag
18854      * @method addInvalidHandleClass
18855      * @param {string} cssClass the class of the elements you wish to ignore
18856      */
18857     addInvalidHandleClass: function(cssClass) {
18858         this.invalidHandleClasses.push(cssClass);
18859     },
18860
18861     /**
18862      * Unsets an excluded tag name set by addInvalidHandleType
18863      * @method removeInvalidHandleType
18864      * @param {string} tagName the type of element to unexclude
18865      */
18866     removeInvalidHandleType: function(tagName) {
18867         var type = tagName.toUpperCase();
18868         // this.invalidHandleTypes[type] = null;
18869         delete this.invalidHandleTypes[type];
18870     },
18871
18872     /**
18873      * Unsets an invalid handle id
18874      * @method removeInvalidHandleId
18875      * @param {string} id the id of the element to re-enable
18876      */
18877     removeInvalidHandleId: function(id) {
18878         if (typeof id !== "string") {
18879             id = Roo.id(id);
18880         }
18881         delete this.invalidHandleIds[id];
18882     },
18883
18884     /**
18885      * Unsets an invalid css class
18886      * @method removeInvalidHandleClass
18887      * @param {string} cssClass the class of the element(s) you wish to
18888      * re-enable
18889      */
18890     removeInvalidHandleClass: function(cssClass) {
18891         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18892             if (this.invalidHandleClasses[i] == cssClass) {
18893                 delete this.invalidHandleClasses[i];
18894             }
18895         }
18896     },
18897
18898     /**
18899      * Checks the tag exclusion list to see if this click should be ignored
18900      * @method isValidHandleChild
18901      * @param {HTMLElement} node the HTMLElement to evaluate
18902      * @return {boolean} true if this is a valid tag type, false if not
18903      */
18904     isValidHandleChild: function(node) {
18905
18906         var valid = true;
18907         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18908         var nodeName;
18909         try {
18910             nodeName = node.nodeName.toUpperCase();
18911         } catch(e) {
18912             nodeName = node.nodeName;
18913         }
18914         valid = valid && !this.invalidHandleTypes[nodeName];
18915         valid = valid && !this.invalidHandleIds[node.id];
18916
18917         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18918             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18919         }
18920
18921
18922         return valid;
18923
18924     },
18925
18926     /**
18927      * Create the array of horizontal tick marks if an interval was specified
18928      * in setXConstraint().
18929      * @method setXTicks
18930      * @private
18931      */
18932     setXTicks: function(iStartX, iTickSize) {
18933         this.xTicks = [];
18934         this.xTickSize = iTickSize;
18935
18936         var tickMap = {};
18937
18938         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18939             if (!tickMap[i]) {
18940                 this.xTicks[this.xTicks.length] = i;
18941                 tickMap[i] = true;
18942             }
18943         }
18944
18945         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18946             if (!tickMap[i]) {
18947                 this.xTicks[this.xTicks.length] = i;
18948                 tickMap[i] = true;
18949             }
18950         }
18951
18952         this.xTicks.sort(this.DDM.numericSort) ;
18953     },
18954
18955     /**
18956      * Create the array of vertical tick marks if an interval was specified in
18957      * setYConstraint().
18958      * @method setYTicks
18959      * @private
18960      */
18961     setYTicks: function(iStartY, iTickSize) {
18962         this.yTicks = [];
18963         this.yTickSize = iTickSize;
18964
18965         var tickMap = {};
18966
18967         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18968             if (!tickMap[i]) {
18969                 this.yTicks[this.yTicks.length] = i;
18970                 tickMap[i] = true;
18971             }
18972         }
18973
18974         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18975             if (!tickMap[i]) {
18976                 this.yTicks[this.yTicks.length] = i;
18977                 tickMap[i] = true;
18978             }
18979         }
18980
18981         this.yTicks.sort(this.DDM.numericSort) ;
18982     },
18983
18984     /**
18985      * By default, the element can be dragged any place on the screen.  Use
18986      * this method to limit the horizontal travel of the element.  Pass in
18987      * 0,0 for the parameters if you want to lock the drag to the y axis.
18988      * @method setXConstraint
18989      * @param {int} iLeft the number of pixels the element can move to the left
18990      * @param {int} iRight the number of pixels the element can move to the
18991      * right
18992      * @param {int} iTickSize optional parameter for specifying that the
18993      * element
18994      * should move iTickSize pixels at a time.
18995      */
18996     setXConstraint: function(iLeft, iRight, iTickSize) {
18997         this.leftConstraint = iLeft;
18998         this.rightConstraint = iRight;
18999
19000         this.minX = this.initPageX - iLeft;
19001         this.maxX = this.initPageX + iRight;
19002         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19003
19004         this.constrainX = true;
19005     },
19006
19007     /**
19008      * Clears any constraints applied to this instance.  Also clears ticks
19009      * since they can't exist independent of a constraint at this time.
19010      * @method clearConstraints
19011      */
19012     clearConstraints: function() {
19013         this.constrainX = false;
19014         this.constrainY = false;
19015         this.clearTicks();
19016     },
19017
19018     /**
19019      * Clears any tick interval defined for this instance
19020      * @method clearTicks
19021      */
19022     clearTicks: function() {
19023         this.xTicks = null;
19024         this.yTicks = null;
19025         this.xTickSize = 0;
19026         this.yTickSize = 0;
19027     },
19028
19029     /**
19030      * By default, the element can be dragged any place on the screen.  Set
19031      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19032      * parameters if you want to lock the drag to the x axis.
19033      * @method setYConstraint
19034      * @param {int} iUp the number of pixels the element can move up
19035      * @param {int} iDown the number of pixels the element can move down
19036      * @param {int} iTickSize optional parameter for specifying that the
19037      * element should move iTickSize pixels at a time.
19038      */
19039     setYConstraint: function(iUp, iDown, iTickSize) {
19040         this.topConstraint = iUp;
19041         this.bottomConstraint = iDown;
19042
19043         this.minY = this.initPageY - iUp;
19044         this.maxY = this.initPageY + iDown;
19045         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19046
19047         this.constrainY = true;
19048
19049     },
19050
19051     /**
19052      * resetConstraints must be called if you manually reposition a dd element.
19053      * @method resetConstraints
19054      * @param {boolean} maintainOffset
19055      */
19056     resetConstraints: function() {
19057
19058
19059         // Maintain offsets if necessary
19060         if (this.initPageX || this.initPageX === 0) {
19061             // figure out how much this thing has moved
19062             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19063             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19064
19065             this.setInitPosition(dx, dy);
19066
19067         // This is the first time we have detected the element's position
19068         } else {
19069             this.setInitPosition();
19070         }
19071
19072         if (this.constrainX) {
19073             this.setXConstraint( this.leftConstraint,
19074                                  this.rightConstraint,
19075                                  this.xTickSize        );
19076         }
19077
19078         if (this.constrainY) {
19079             this.setYConstraint( this.topConstraint,
19080                                  this.bottomConstraint,
19081                                  this.yTickSize         );
19082         }
19083     },
19084
19085     /**
19086      * Normally the drag element is moved pixel by pixel, but we can specify
19087      * that it move a number of pixels at a time.  This method resolves the
19088      * location when we have it set up like this.
19089      * @method getTick
19090      * @param {int} val where we want to place the object
19091      * @param {int[]} tickArray sorted array of valid points
19092      * @return {int} the closest tick
19093      * @private
19094      */
19095     getTick: function(val, tickArray) {
19096
19097         if (!tickArray) {
19098             // If tick interval is not defined, it is effectively 1 pixel,
19099             // so we return the value passed to us.
19100             return val;
19101         } else if (tickArray[0] >= val) {
19102             // The value is lower than the first tick, so we return the first
19103             // tick.
19104             return tickArray[0];
19105         } else {
19106             for (var i=0, len=tickArray.length; i<len; ++i) {
19107                 var next = i + 1;
19108                 if (tickArray[next] && tickArray[next] >= val) {
19109                     var diff1 = val - tickArray[i];
19110                     var diff2 = tickArray[next] - val;
19111                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19112                 }
19113             }
19114
19115             // The value is larger than the last tick, so we return the last
19116             // tick.
19117             return tickArray[tickArray.length - 1];
19118         }
19119     },
19120
19121     /**
19122      * toString method
19123      * @method toString
19124      * @return {string} string representation of the dd obj
19125      */
19126     toString: function() {
19127         return ("DragDrop " + this.id);
19128     }
19129
19130 });
19131
19132 })();
19133 /*
19134  * Based on:
19135  * Ext JS Library 1.1.1
19136  * Copyright(c) 2006-2007, Ext JS, LLC.
19137  *
19138  * Originally Released Under LGPL - original licence link has changed is not relivant.
19139  *
19140  * Fork - LGPL
19141  * <script type="text/javascript">
19142  */
19143
19144
19145 /**
19146  * The drag and drop utility provides a framework for building drag and drop
19147  * applications.  In addition to enabling drag and drop for specific elements,
19148  * the drag and drop elements are tracked by the manager class, and the
19149  * interactions between the various elements are tracked during the drag and
19150  * the implementing code is notified about these important moments.
19151  */
19152
19153 // Only load the library once.  Rewriting the manager class would orphan
19154 // existing drag and drop instances.
19155 if (!Roo.dd.DragDropMgr) {
19156
19157 /**
19158  * @class Roo.dd.DragDropMgr
19159  * DragDropMgr is a singleton that tracks the element interaction for
19160  * all DragDrop items in the window.  Generally, you will not call
19161  * this class directly, but it does have helper methods that could
19162  * be useful in your DragDrop implementations.
19163  * @singleton
19164  */
19165 Roo.dd.DragDropMgr = function() {
19166
19167     var Event = Roo.EventManager;
19168
19169     return {
19170
19171         /**
19172          * Two dimensional Array of registered DragDrop objects.  The first
19173          * dimension is the DragDrop item group, the second the DragDrop
19174          * object.
19175          * @property ids
19176          * @type {string: string}
19177          * @private
19178          * @static
19179          */
19180         ids: {},
19181
19182         /**
19183          * Array of element ids defined as drag handles.  Used to determine
19184          * if the element that generated the mousedown event is actually the
19185          * handle and not the html element itself.
19186          * @property handleIds
19187          * @type {string: string}
19188          * @private
19189          * @static
19190          */
19191         handleIds: {},
19192
19193         /**
19194          * the DragDrop object that is currently being dragged
19195          * @property dragCurrent
19196          * @type DragDrop
19197          * @private
19198          * @static
19199          **/
19200         dragCurrent: null,
19201
19202         /**
19203          * the DragDrop object(s) that are being hovered over
19204          * @property dragOvers
19205          * @type Array
19206          * @private
19207          * @static
19208          */
19209         dragOvers: {},
19210
19211         /**
19212          * the X distance between the cursor and the object being dragged
19213          * @property deltaX
19214          * @type int
19215          * @private
19216          * @static
19217          */
19218         deltaX: 0,
19219
19220         /**
19221          * the Y distance between the cursor and the object being dragged
19222          * @property deltaY
19223          * @type int
19224          * @private
19225          * @static
19226          */
19227         deltaY: 0,
19228
19229         /**
19230          * Flag to determine if we should prevent the default behavior of the
19231          * events we define. By default this is true, but this can be set to
19232          * false if you need the default behavior (not recommended)
19233          * @property preventDefault
19234          * @type boolean
19235          * @static
19236          */
19237         preventDefault: true,
19238
19239         /**
19240          * Flag to determine if we should stop the propagation of the events
19241          * we generate. This is true by default but you may want to set it to
19242          * false if the html element contains other features that require the
19243          * mouse click.
19244          * @property stopPropagation
19245          * @type boolean
19246          * @static
19247          */
19248         stopPropagation: true,
19249
19250         /**
19251          * Internal flag that is set to true when drag and drop has been
19252          * intialized
19253          * @property initialized
19254          * @private
19255          * @static
19256          */
19257         initalized: false,
19258
19259         /**
19260          * All drag and drop can be disabled.
19261          * @property locked
19262          * @private
19263          * @static
19264          */
19265         locked: false,
19266
19267         /**
19268          * Called the first time an element is registered.
19269          * @method init
19270          * @private
19271          * @static
19272          */
19273         init: function() {
19274             this.initialized = true;
19275         },
19276
19277         /**
19278          * In point mode, drag and drop interaction is defined by the
19279          * location of the cursor during the drag/drop
19280          * @property POINT
19281          * @type int
19282          * @static
19283          */
19284         POINT: 0,
19285
19286         /**
19287          * In intersect mode, drag and drop interactio nis defined by the
19288          * overlap of two or more drag and drop objects.
19289          * @property INTERSECT
19290          * @type int
19291          * @static
19292          */
19293         INTERSECT: 1,
19294
19295         /**
19296          * The current drag and drop mode.  Default: POINT
19297          * @property mode
19298          * @type int
19299          * @static
19300          */
19301         mode: 0,
19302
19303         /**
19304          * Runs method on all drag and drop objects
19305          * @method _execOnAll
19306          * @private
19307          * @static
19308          */
19309         _execOnAll: function(sMethod, args) {
19310             for (var i in this.ids) {
19311                 for (var j in this.ids[i]) {
19312                     var oDD = this.ids[i][j];
19313                     if (! this.isTypeOfDD(oDD)) {
19314                         continue;
19315                     }
19316                     oDD[sMethod].apply(oDD, args);
19317                 }
19318             }
19319         },
19320
19321         /**
19322          * Drag and drop initialization.  Sets up the global event handlers
19323          * @method _onLoad
19324          * @private
19325          * @static
19326          */
19327         _onLoad: function() {
19328
19329             this.init();
19330
19331             if (!Roo.isTouch) {
19332                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19333                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19334             }
19335             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19336             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19337             
19338             Event.on(window,   "unload",    this._onUnload, this, true);
19339             Event.on(window,   "resize",    this._onResize, this, true);
19340             // Event.on(window,   "mouseout",    this._test);
19341
19342         },
19343
19344         /**
19345          * Reset constraints on all drag and drop objs
19346          * @method _onResize
19347          * @private
19348          * @static
19349          */
19350         _onResize: function(e) {
19351             this._execOnAll("resetConstraints", []);
19352         },
19353
19354         /**
19355          * Lock all drag and drop functionality
19356          * @method lock
19357          * @static
19358          */
19359         lock: function() { this.locked = true; },
19360
19361         /**
19362          * Unlock all drag and drop functionality
19363          * @method unlock
19364          * @static
19365          */
19366         unlock: function() { this.locked = false; },
19367
19368         /**
19369          * Is drag and drop locked?
19370          * @method isLocked
19371          * @return {boolean} True if drag and drop is locked, false otherwise.
19372          * @static
19373          */
19374         isLocked: function() { return this.locked; },
19375
19376         /**
19377          * Location cache that is set for all drag drop objects when a drag is
19378          * initiated, cleared when the drag is finished.
19379          * @property locationCache
19380          * @private
19381          * @static
19382          */
19383         locationCache: {},
19384
19385         /**
19386          * Set useCache to false if you want to force object the lookup of each
19387          * drag and drop linked element constantly during a drag.
19388          * @property useCache
19389          * @type boolean
19390          * @static
19391          */
19392         useCache: true,
19393
19394         /**
19395          * The number of pixels that the mouse needs to move after the
19396          * mousedown before the drag is initiated.  Default=3;
19397          * @property clickPixelThresh
19398          * @type int
19399          * @static
19400          */
19401         clickPixelThresh: 3,
19402
19403         /**
19404          * The number of milliseconds after the mousedown event to initiate the
19405          * drag if we don't get a mouseup event. Default=1000
19406          * @property clickTimeThresh
19407          * @type int
19408          * @static
19409          */
19410         clickTimeThresh: 350,
19411
19412         /**
19413          * Flag that indicates that either the drag pixel threshold or the
19414          * mousdown time threshold has been met
19415          * @property dragThreshMet
19416          * @type boolean
19417          * @private
19418          * @static
19419          */
19420         dragThreshMet: false,
19421
19422         /**
19423          * Timeout used for the click time threshold
19424          * @property clickTimeout
19425          * @type Object
19426          * @private
19427          * @static
19428          */
19429         clickTimeout: null,
19430
19431         /**
19432          * The X position of the mousedown event stored for later use when a
19433          * drag threshold is met.
19434          * @property startX
19435          * @type int
19436          * @private
19437          * @static
19438          */
19439         startX: 0,
19440
19441         /**
19442          * The Y position of the mousedown event stored for later use when a
19443          * drag threshold is met.
19444          * @property startY
19445          * @type int
19446          * @private
19447          * @static
19448          */
19449         startY: 0,
19450
19451         /**
19452          * Each DragDrop instance must be registered with the DragDropMgr.
19453          * This is executed in DragDrop.init()
19454          * @method regDragDrop
19455          * @param {DragDrop} oDD the DragDrop object to register
19456          * @param {String} sGroup the name of the group this element belongs to
19457          * @static
19458          */
19459         regDragDrop: function(oDD, sGroup) {
19460             if (!this.initialized) { this.init(); }
19461
19462             if (!this.ids[sGroup]) {
19463                 this.ids[sGroup] = {};
19464             }
19465             this.ids[sGroup][oDD.id] = oDD;
19466         },
19467
19468         /**
19469          * Removes the supplied dd instance from the supplied group. Executed
19470          * by DragDrop.removeFromGroup, so don't call this function directly.
19471          * @method removeDDFromGroup
19472          * @private
19473          * @static
19474          */
19475         removeDDFromGroup: function(oDD, sGroup) {
19476             if (!this.ids[sGroup]) {
19477                 this.ids[sGroup] = {};
19478             }
19479
19480             var obj = this.ids[sGroup];
19481             if (obj && obj[oDD.id]) {
19482                 delete obj[oDD.id];
19483             }
19484         },
19485
19486         /**
19487          * Unregisters a drag and drop item.  This is executed in
19488          * DragDrop.unreg, use that method instead of calling this directly.
19489          * @method _remove
19490          * @private
19491          * @static
19492          */
19493         _remove: function(oDD) {
19494             for (var g in oDD.groups) {
19495                 if (g && this.ids[g][oDD.id]) {
19496                     delete this.ids[g][oDD.id];
19497                 }
19498             }
19499             delete this.handleIds[oDD.id];
19500         },
19501
19502         /**
19503          * Each DragDrop handle element must be registered.  This is done
19504          * automatically when executing DragDrop.setHandleElId()
19505          * @method regHandle
19506          * @param {String} sDDId the DragDrop id this element is a handle for
19507          * @param {String} sHandleId the id of the element that is the drag
19508          * handle
19509          * @static
19510          */
19511         regHandle: function(sDDId, sHandleId) {
19512             if (!this.handleIds[sDDId]) {
19513                 this.handleIds[sDDId] = {};
19514             }
19515             this.handleIds[sDDId][sHandleId] = sHandleId;
19516         },
19517
19518         /**
19519          * Utility function to determine if a given element has been
19520          * registered as a drag drop item.
19521          * @method isDragDrop
19522          * @param {String} id the element id to check
19523          * @return {boolean} true if this element is a DragDrop item,
19524          * false otherwise
19525          * @static
19526          */
19527         isDragDrop: function(id) {
19528             return ( this.getDDById(id) ) ? true : false;
19529         },
19530
19531         /**
19532          * Returns the drag and drop instances that are in all groups the
19533          * passed in instance belongs to.
19534          * @method getRelated
19535          * @param {DragDrop} p_oDD the obj to get related data for
19536          * @param {boolean} bTargetsOnly if true, only return targetable objs
19537          * @return {DragDrop[]} the related instances
19538          * @static
19539          */
19540         getRelated: function(p_oDD, bTargetsOnly) {
19541             var oDDs = [];
19542             for (var i in p_oDD.groups) {
19543                 for (j in this.ids[i]) {
19544                     var dd = this.ids[i][j];
19545                     if (! this.isTypeOfDD(dd)) {
19546                         continue;
19547                     }
19548                     if (!bTargetsOnly || dd.isTarget) {
19549                         oDDs[oDDs.length] = dd;
19550                     }
19551                 }
19552             }
19553
19554             return oDDs;
19555         },
19556
19557         /**
19558          * Returns true if the specified dd target is a legal target for
19559          * the specifice drag obj
19560          * @method isLegalTarget
19561          * @param {DragDrop} the drag obj
19562          * @param {DragDrop} the target
19563          * @return {boolean} true if the target is a legal target for the
19564          * dd obj
19565          * @static
19566          */
19567         isLegalTarget: function (oDD, oTargetDD) {
19568             var targets = this.getRelated(oDD, true);
19569             for (var i=0, len=targets.length;i<len;++i) {
19570                 if (targets[i].id == oTargetDD.id) {
19571                     return true;
19572                 }
19573             }
19574
19575             return false;
19576         },
19577
19578         /**
19579          * My goal is to be able to transparently determine if an object is
19580          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19581          * returns "object", oDD.constructor.toString() always returns
19582          * "DragDrop" and not the name of the subclass.  So for now it just
19583          * evaluates a well-known variable in DragDrop.
19584          * @method isTypeOfDD
19585          * @param {Object} the object to evaluate
19586          * @return {boolean} true if typeof oDD = DragDrop
19587          * @static
19588          */
19589         isTypeOfDD: function (oDD) {
19590             return (oDD && oDD.__ygDragDrop);
19591         },
19592
19593         /**
19594          * Utility function to determine if a given element has been
19595          * registered as a drag drop handle for the given Drag Drop object.
19596          * @method isHandle
19597          * @param {String} id the element id to check
19598          * @return {boolean} true if this element is a DragDrop handle, false
19599          * otherwise
19600          * @static
19601          */
19602         isHandle: function(sDDId, sHandleId) {
19603             return ( this.handleIds[sDDId] &&
19604                             this.handleIds[sDDId][sHandleId] );
19605         },
19606
19607         /**
19608          * Returns the DragDrop instance for a given id
19609          * @method getDDById
19610          * @param {String} id the id of the DragDrop object
19611          * @return {DragDrop} the drag drop object, null if it is not found
19612          * @static
19613          */
19614         getDDById: function(id) {
19615             for (var i in this.ids) {
19616                 if (this.ids[i][id]) {
19617                     return this.ids[i][id];
19618                 }
19619             }
19620             return null;
19621         },
19622
19623         /**
19624          * Fired after a registered DragDrop object gets the mousedown event.
19625          * Sets up the events required to track the object being dragged
19626          * @method handleMouseDown
19627          * @param {Event} e the event
19628          * @param oDD the DragDrop object being dragged
19629          * @private
19630          * @static
19631          */
19632         handleMouseDown: function(e, oDD) {
19633             if(Roo.QuickTips){
19634                 Roo.QuickTips.disable();
19635             }
19636             this.currentTarget = e.getTarget();
19637
19638             this.dragCurrent = oDD;
19639
19640             var el = oDD.getEl();
19641
19642             // track start position
19643             this.startX = e.getPageX();
19644             this.startY = e.getPageY();
19645
19646             this.deltaX = this.startX - el.offsetLeft;
19647             this.deltaY = this.startY - el.offsetTop;
19648
19649             this.dragThreshMet = false;
19650
19651             this.clickTimeout = setTimeout(
19652                     function() {
19653                         var DDM = Roo.dd.DDM;
19654                         DDM.startDrag(DDM.startX, DDM.startY);
19655                     },
19656                     this.clickTimeThresh );
19657         },
19658
19659         /**
19660          * Fired when either the drag pixel threshol or the mousedown hold
19661          * time threshold has been met.
19662          * @method startDrag
19663          * @param x {int} the X position of the original mousedown
19664          * @param y {int} the Y position of the original mousedown
19665          * @static
19666          */
19667         startDrag: function(x, y) {
19668             clearTimeout(this.clickTimeout);
19669             if (this.dragCurrent) {
19670                 this.dragCurrent.b4StartDrag(x, y);
19671                 this.dragCurrent.startDrag(x, y);
19672             }
19673             this.dragThreshMet = true;
19674         },
19675
19676         /**
19677          * Internal function to handle the mouseup event.  Will be invoked
19678          * from the context of the document.
19679          * @method handleMouseUp
19680          * @param {Event} e the event
19681          * @private
19682          * @static
19683          */
19684         handleMouseUp: function(e) {
19685
19686             if(Roo.QuickTips){
19687                 Roo.QuickTips.enable();
19688             }
19689             if (! this.dragCurrent) {
19690                 return;
19691             }
19692
19693             clearTimeout(this.clickTimeout);
19694
19695             if (this.dragThreshMet) {
19696                 this.fireEvents(e, true);
19697             } else {
19698             }
19699
19700             this.stopDrag(e);
19701
19702             this.stopEvent(e);
19703         },
19704
19705         /**
19706          * Utility to stop event propagation and event default, if these
19707          * features are turned on.
19708          * @method stopEvent
19709          * @param {Event} e the event as returned by this.getEvent()
19710          * @static
19711          */
19712         stopEvent: function(e){
19713             if(this.stopPropagation) {
19714                 e.stopPropagation();
19715             }
19716
19717             if (this.preventDefault) {
19718                 e.preventDefault();
19719             }
19720         },
19721
19722         /**
19723          * Internal function to clean up event handlers after the drag
19724          * operation is complete
19725          * @method stopDrag
19726          * @param {Event} e the event
19727          * @private
19728          * @static
19729          */
19730         stopDrag: function(e) {
19731             // Fire the drag end event for the item that was dragged
19732             if (this.dragCurrent) {
19733                 if (this.dragThreshMet) {
19734                     this.dragCurrent.b4EndDrag(e);
19735                     this.dragCurrent.endDrag(e);
19736                 }
19737
19738                 this.dragCurrent.onMouseUp(e);
19739             }
19740
19741             this.dragCurrent = null;
19742             this.dragOvers = {};
19743         },
19744
19745         /**
19746          * Internal function to handle the mousemove event.  Will be invoked
19747          * from the context of the html element.
19748          *
19749          * @TODO figure out what we can do about mouse events lost when the
19750          * user drags objects beyond the window boundary.  Currently we can
19751          * detect this in internet explorer by verifying that the mouse is
19752          * down during the mousemove event.  Firefox doesn't give us the
19753          * button state on the mousemove event.
19754          * @method handleMouseMove
19755          * @param {Event} e the event
19756          * @private
19757          * @static
19758          */
19759         handleMouseMove: function(e) {
19760             if (! this.dragCurrent) {
19761                 return true;
19762             }
19763
19764             // var button = e.which || e.button;
19765
19766             // check for IE mouseup outside of page boundary
19767             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19768                 this.stopEvent(e);
19769                 return this.handleMouseUp(e);
19770             }
19771
19772             if (!this.dragThreshMet) {
19773                 var diffX = Math.abs(this.startX - e.getPageX());
19774                 var diffY = Math.abs(this.startY - e.getPageY());
19775                 if (diffX > this.clickPixelThresh ||
19776                             diffY > this.clickPixelThresh) {
19777                     this.startDrag(this.startX, this.startY);
19778                 }
19779             }
19780
19781             if (this.dragThreshMet) {
19782                 this.dragCurrent.b4Drag(e);
19783                 this.dragCurrent.onDrag(e);
19784                 if(!this.dragCurrent.moveOnly){
19785                     this.fireEvents(e, false);
19786                 }
19787             }
19788
19789             this.stopEvent(e);
19790
19791             return true;
19792         },
19793
19794         /**
19795          * Iterates over all of the DragDrop elements to find ones we are
19796          * hovering over or dropping on
19797          * @method fireEvents
19798          * @param {Event} e the event
19799          * @param {boolean} isDrop is this a drop op or a mouseover op?
19800          * @private
19801          * @static
19802          */
19803         fireEvents: function(e, isDrop) {
19804             var dc = this.dragCurrent;
19805
19806             // If the user did the mouse up outside of the window, we could
19807             // get here even though we have ended the drag.
19808             if (!dc || dc.isLocked()) {
19809                 return;
19810             }
19811
19812             var pt = e.getPoint();
19813
19814             // cache the previous dragOver array
19815             var oldOvers = [];
19816
19817             var outEvts   = [];
19818             var overEvts  = [];
19819             var dropEvts  = [];
19820             var enterEvts = [];
19821
19822             // Check to see if the object(s) we were hovering over is no longer
19823             // being hovered over so we can fire the onDragOut event
19824             for (var i in this.dragOvers) {
19825
19826                 var ddo = this.dragOvers[i];
19827
19828                 if (! this.isTypeOfDD(ddo)) {
19829                     continue;
19830                 }
19831
19832                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19833                     outEvts.push( ddo );
19834                 }
19835
19836                 oldOvers[i] = true;
19837                 delete this.dragOvers[i];
19838             }
19839
19840             for (var sGroup in dc.groups) {
19841
19842                 if ("string" != typeof sGroup) {
19843                     continue;
19844                 }
19845
19846                 for (i in this.ids[sGroup]) {
19847                     var oDD = this.ids[sGroup][i];
19848                     if (! this.isTypeOfDD(oDD)) {
19849                         continue;
19850                     }
19851
19852                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19853                         if (this.isOverTarget(pt, oDD, this.mode)) {
19854                             // look for drop interactions
19855                             if (isDrop) {
19856                                 dropEvts.push( oDD );
19857                             // look for drag enter and drag over interactions
19858                             } else {
19859
19860                                 // initial drag over: dragEnter fires
19861                                 if (!oldOvers[oDD.id]) {
19862                                     enterEvts.push( oDD );
19863                                 // subsequent drag overs: dragOver fires
19864                                 } else {
19865                                     overEvts.push( oDD );
19866                                 }
19867
19868                                 this.dragOvers[oDD.id] = oDD;
19869                             }
19870                         }
19871                     }
19872                 }
19873             }
19874
19875             if (this.mode) {
19876                 if (outEvts.length) {
19877                     dc.b4DragOut(e, outEvts);
19878                     dc.onDragOut(e, outEvts);
19879                 }
19880
19881                 if (enterEvts.length) {
19882                     dc.onDragEnter(e, enterEvts);
19883                 }
19884
19885                 if (overEvts.length) {
19886                     dc.b4DragOver(e, overEvts);
19887                     dc.onDragOver(e, overEvts);
19888                 }
19889
19890                 if (dropEvts.length) {
19891                     dc.b4DragDrop(e, dropEvts);
19892                     dc.onDragDrop(e, dropEvts);
19893                 }
19894
19895             } else {
19896                 // fire dragout events
19897                 var len = 0;
19898                 for (i=0, len=outEvts.length; i<len; ++i) {
19899                     dc.b4DragOut(e, outEvts[i].id);
19900                     dc.onDragOut(e, outEvts[i].id);
19901                 }
19902
19903                 // fire enter events
19904                 for (i=0,len=enterEvts.length; i<len; ++i) {
19905                     // dc.b4DragEnter(e, oDD.id);
19906                     dc.onDragEnter(e, enterEvts[i].id);
19907                 }
19908
19909                 // fire over events
19910                 for (i=0,len=overEvts.length; i<len; ++i) {
19911                     dc.b4DragOver(e, overEvts[i].id);
19912                     dc.onDragOver(e, overEvts[i].id);
19913                 }
19914
19915                 // fire drop events
19916                 for (i=0, len=dropEvts.length; i<len; ++i) {
19917                     dc.b4DragDrop(e, dropEvts[i].id);
19918                     dc.onDragDrop(e, dropEvts[i].id);
19919                 }
19920
19921             }
19922
19923             // notify about a drop that did not find a target
19924             if (isDrop && !dropEvts.length) {
19925                 dc.onInvalidDrop(e);
19926             }
19927
19928         },
19929
19930         /**
19931          * Helper function for getting the best match from the list of drag
19932          * and drop objects returned by the drag and drop events when we are
19933          * in INTERSECT mode.  It returns either the first object that the
19934          * cursor is over, or the object that has the greatest overlap with
19935          * the dragged element.
19936          * @method getBestMatch
19937          * @param  {DragDrop[]} dds The array of drag and drop objects
19938          * targeted
19939          * @return {DragDrop}       The best single match
19940          * @static
19941          */
19942         getBestMatch: function(dds) {
19943             var winner = null;
19944             // Return null if the input is not what we expect
19945             //if (!dds || !dds.length || dds.length == 0) {
19946                // winner = null;
19947             // If there is only one item, it wins
19948             //} else if (dds.length == 1) {
19949
19950             var len = dds.length;
19951
19952             if (len == 1) {
19953                 winner = dds[0];
19954             } else {
19955                 // Loop through the targeted items
19956                 for (var i=0; i<len; ++i) {
19957                     var dd = dds[i];
19958                     // If the cursor is over the object, it wins.  If the
19959                     // cursor is over multiple matches, the first one we come
19960                     // to wins.
19961                     if (dd.cursorIsOver) {
19962                         winner = dd;
19963                         break;
19964                     // Otherwise the object with the most overlap wins
19965                     } else {
19966                         if (!winner ||
19967                             winner.overlap.getArea() < dd.overlap.getArea()) {
19968                             winner = dd;
19969                         }
19970                     }
19971                 }
19972             }
19973
19974             return winner;
19975         },
19976
19977         /**
19978          * Refreshes the cache of the top-left and bottom-right points of the
19979          * drag and drop objects in the specified group(s).  This is in the
19980          * format that is stored in the drag and drop instance, so typical
19981          * usage is:
19982          * <code>
19983          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19984          * </code>
19985          * Alternatively:
19986          * <code>
19987          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19988          * </code>
19989          * @TODO this really should be an indexed array.  Alternatively this
19990          * method could accept both.
19991          * @method refreshCache
19992          * @param {Object} groups an associative array of groups to refresh
19993          * @static
19994          */
19995         refreshCache: function(groups) {
19996             for (var sGroup in groups) {
19997                 if ("string" != typeof sGroup) {
19998                     continue;
19999                 }
20000                 for (var i in this.ids[sGroup]) {
20001                     var oDD = this.ids[sGroup][i];
20002
20003                     if (this.isTypeOfDD(oDD)) {
20004                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20005                         var loc = this.getLocation(oDD);
20006                         if (loc) {
20007                             this.locationCache[oDD.id] = loc;
20008                         } else {
20009                             delete this.locationCache[oDD.id];
20010                             // this will unregister the drag and drop object if
20011                             // the element is not in a usable state
20012                             // oDD.unreg();
20013                         }
20014                     }
20015                 }
20016             }
20017         },
20018
20019         /**
20020          * This checks to make sure an element exists and is in the DOM.  The
20021          * main purpose is to handle cases where innerHTML is used to remove
20022          * drag and drop objects from the DOM.  IE provides an 'unspecified
20023          * error' when trying to access the offsetParent of such an element
20024          * @method verifyEl
20025          * @param {HTMLElement} el the element to check
20026          * @return {boolean} true if the element looks usable
20027          * @static
20028          */
20029         verifyEl: function(el) {
20030             if (el) {
20031                 var parent;
20032                 if(Roo.isIE){
20033                     try{
20034                         parent = el.offsetParent;
20035                     }catch(e){}
20036                 }else{
20037                     parent = el.offsetParent;
20038                 }
20039                 if (parent) {
20040                     return true;
20041                 }
20042             }
20043
20044             return false;
20045         },
20046
20047         /**
20048          * Returns a Region object containing the drag and drop element's position
20049          * and size, including the padding configured for it
20050          * @method getLocation
20051          * @param {DragDrop} oDD the drag and drop object to get the
20052          *                       location for
20053          * @return {Roo.lib.Region} a Region object representing the total area
20054          *                             the element occupies, including any padding
20055          *                             the instance is configured for.
20056          * @static
20057          */
20058         getLocation: function(oDD) {
20059             if (! this.isTypeOfDD(oDD)) {
20060                 return null;
20061             }
20062
20063             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20064
20065             try {
20066                 pos= Roo.lib.Dom.getXY(el);
20067             } catch (e) { }
20068
20069             if (!pos) {
20070                 return null;
20071             }
20072
20073             x1 = pos[0];
20074             x2 = x1 + el.offsetWidth;
20075             y1 = pos[1];
20076             y2 = y1 + el.offsetHeight;
20077
20078             t = y1 - oDD.padding[0];
20079             r = x2 + oDD.padding[1];
20080             b = y2 + oDD.padding[2];
20081             l = x1 - oDD.padding[3];
20082
20083             return new Roo.lib.Region( t, r, b, l );
20084         },
20085
20086         /**
20087          * Checks the cursor location to see if it over the target
20088          * @method isOverTarget
20089          * @param {Roo.lib.Point} pt The point to evaluate
20090          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20091          * @return {boolean} true if the mouse is over the target
20092          * @private
20093          * @static
20094          */
20095         isOverTarget: function(pt, oTarget, intersect) {
20096             // use cache if available
20097             var loc = this.locationCache[oTarget.id];
20098             if (!loc || !this.useCache) {
20099                 loc = this.getLocation(oTarget);
20100                 this.locationCache[oTarget.id] = loc;
20101
20102             }
20103
20104             if (!loc) {
20105                 return false;
20106             }
20107
20108             oTarget.cursorIsOver = loc.contains( pt );
20109
20110             // DragDrop is using this as a sanity check for the initial mousedown
20111             // in this case we are done.  In POINT mode, if the drag obj has no
20112             // contraints, we are also done. Otherwise we need to evaluate the
20113             // location of the target as related to the actual location of the
20114             // dragged element.
20115             var dc = this.dragCurrent;
20116             if (!dc || !dc.getTargetCoord ||
20117                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20118                 return oTarget.cursorIsOver;
20119             }
20120
20121             oTarget.overlap = null;
20122
20123             // Get the current location of the drag element, this is the
20124             // location of the mouse event less the delta that represents
20125             // where the original mousedown happened on the element.  We
20126             // need to consider constraints and ticks as well.
20127             var pos = dc.getTargetCoord(pt.x, pt.y);
20128
20129             var el = dc.getDragEl();
20130             var curRegion = new Roo.lib.Region( pos.y,
20131                                                    pos.x + el.offsetWidth,
20132                                                    pos.y + el.offsetHeight,
20133                                                    pos.x );
20134
20135             var overlap = curRegion.intersect(loc);
20136
20137             if (overlap) {
20138                 oTarget.overlap = overlap;
20139                 return (intersect) ? true : oTarget.cursorIsOver;
20140             } else {
20141                 return false;
20142             }
20143         },
20144
20145         /**
20146          * unload event handler
20147          * @method _onUnload
20148          * @private
20149          * @static
20150          */
20151         _onUnload: function(e, me) {
20152             Roo.dd.DragDropMgr.unregAll();
20153         },
20154
20155         /**
20156          * Cleans up the drag and drop events and objects.
20157          * @method unregAll
20158          * @private
20159          * @static
20160          */
20161         unregAll: function() {
20162
20163             if (this.dragCurrent) {
20164                 this.stopDrag();
20165                 this.dragCurrent = null;
20166             }
20167
20168             this._execOnAll("unreg", []);
20169
20170             for (i in this.elementCache) {
20171                 delete this.elementCache[i];
20172             }
20173
20174             this.elementCache = {};
20175             this.ids = {};
20176         },
20177
20178         /**
20179          * A cache of DOM elements
20180          * @property elementCache
20181          * @private
20182          * @static
20183          */
20184         elementCache: {},
20185
20186         /**
20187          * Get the wrapper for the DOM element specified
20188          * @method getElWrapper
20189          * @param {String} id the id of the element to get
20190          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20191          * @private
20192          * @deprecated This wrapper isn't that useful
20193          * @static
20194          */
20195         getElWrapper: function(id) {
20196             var oWrapper = this.elementCache[id];
20197             if (!oWrapper || !oWrapper.el) {
20198                 oWrapper = this.elementCache[id] =
20199                     new this.ElementWrapper(Roo.getDom(id));
20200             }
20201             return oWrapper;
20202         },
20203
20204         /**
20205          * Returns the actual DOM element
20206          * @method getElement
20207          * @param {String} id the id of the elment to get
20208          * @return {Object} The element
20209          * @deprecated use Roo.getDom instead
20210          * @static
20211          */
20212         getElement: function(id) {
20213             return Roo.getDom(id);
20214         },
20215
20216         /**
20217          * Returns the style property for the DOM element (i.e.,
20218          * document.getElById(id).style)
20219          * @method getCss
20220          * @param {String} id the id of the elment to get
20221          * @return {Object} The style property of the element
20222          * @deprecated use Roo.getDom instead
20223          * @static
20224          */
20225         getCss: function(id) {
20226             var el = Roo.getDom(id);
20227             return (el) ? el.style : null;
20228         },
20229
20230         /**
20231          * Inner class for cached elements
20232          * @class DragDropMgr.ElementWrapper
20233          * @for DragDropMgr
20234          * @private
20235          * @deprecated
20236          */
20237         ElementWrapper: function(el) {
20238                 /**
20239                  * The element
20240                  * @property el
20241                  */
20242                 this.el = el || null;
20243                 /**
20244                  * The element id
20245                  * @property id
20246                  */
20247                 this.id = this.el && el.id;
20248                 /**
20249                  * A reference to the style property
20250                  * @property css
20251                  */
20252                 this.css = this.el && el.style;
20253             },
20254
20255         /**
20256          * Returns the X position of an html element
20257          * @method getPosX
20258          * @param el the element for which to get the position
20259          * @return {int} the X coordinate
20260          * @for DragDropMgr
20261          * @deprecated use Roo.lib.Dom.getX instead
20262          * @static
20263          */
20264         getPosX: function(el) {
20265             return Roo.lib.Dom.getX(el);
20266         },
20267
20268         /**
20269          * Returns the Y position of an html element
20270          * @method getPosY
20271          * @param el the element for which to get the position
20272          * @return {int} the Y coordinate
20273          * @deprecated use Roo.lib.Dom.getY instead
20274          * @static
20275          */
20276         getPosY: function(el) {
20277             return Roo.lib.Dom.getY(el);
20278         },
20279
20280         /**
20281          * Swap two nodes.  In IE, we use the native method, for others we
20282          * emulate the IE behavior
20283          * @method swapNode
20284          * @param n1 the first node to swap
20285          * @param n2 the other node to swap
20286          * @static
20287          */
20288         swapNode: function(n1, n2) {
20289             if (n1.swapNode) {
20290                 n1.swapNode(n2);
20291             } else {
20292                 var p = n2.parentNode;
20293                 var s = n2.nextSibling;
20294
20295                 if (s == n1) {
20296                     p.insertBefore(n1, n2);
20297                 } else if (n2 == n1.nextSibling) {
20298                     p.insertBefore(n2, n1);
20299                 } else {
20300                     n1.parentNode.replaceChild(n2, n1);
20301                     p.insertBefore(n1, s);
20302                 }
20303             }
20304         },
20305
20306         /**
20307          * Returns the current scroll position
20308          * @method getScroll
20309          * @private
20310          * @static
20311          */
20312         getScroll: function () {
20313             var t, l, dde=document.documentElement, db=document.body;
20314             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20315                 t = dde.scrollTop;
20316                 l = dde.scrollLeft;
20317             } else if (db) {
20318                 t = db.scrollTop;
20319                 l = db.scrollLeft;
20320             } else {
20321
20322             }
20323             return { top: t, left: l };
20324         },
20325
20326         /**
20327          * Returns the specified element style property
20328          * @method getStyle
20329          * @param {HTMLElement} el          the element
20330          * @param {string}      styleProp   the style property
20331          * @return {string} The value of the style property
20332          * @deprecated use Roo.lib.Dom.getStyle
20333          * @static
20334          */
20335         getStyle: function(el, styleProp) {
20336             return Roo.fly(el).getStyle(styleProp);
20337         },
20338
20339         /**
20340          * Gets the scrollTop
20341          * @method getScrollTop
20342          * @return {int} the document's scrollTop
20343          * @static
20344          */
20345         getScrollTop: function () { return this.getScroll().top; },
20346
20347         /**
20348          * Gets the scrollLeft
20349          * @method getScrollLeft
20350          * @return {int} the document's scrollTop
20351          * @static
20352          */
20353         getScrollLeft: function () { return this.getScroll().left; },
20354
20355         /**
20356          * Sets the x/y position of an element to the location of the
20357          * target element.
20358          * @method moveToEl
20359          * @param {HTMLElement} moveEl      The element to move
20360          * @param {HTMLElement} targetEl    The position reference element
20361          * @static
20362          */
20363         moveToEl: function (moveEl, targetEl) {
20364             var aCoord = Roo.lib.Dom.getXY(targetEl);
20365             Roo.lib.Dom.setXY(moveEl, aCoord);
20366         },
20367
20368         /**
20369          * Numeric array sort function
20370          * @method numericSort
20371          * @static
20372          */
20373         numericSort: function(a, b) { return (a - b); },
20374
20375         /**
20376          * Internal counter
20377          * @property _timeoutCount
20378          * @private
20379          * @static
20380          */
20381         _timeoutCount: 0,
20382
20383         /**
20384          * Trying to make the load order less important.  Without this we get
20385          * an error if this file is loaded before the Event Utility.
20386          * @method _addListeners
20387          * @private
20388          * @static
20389          */
20390         _addListeners: function() {
20391             var DDM = Roo.dd.DDM;
20392             if ( Roo.lib.Event && document ) {
20393                 DDM._onLoad();
20394             } else {
20395                 if (DDM._timeoutCount > 2000) {
20396                 } else {
20397                     setTimeout(DDM._addListeners, 10);
20398                     if (document && document.body) {
20399                         DDM._timeoutCount += 1;
20400                     }
20401                 }
20402             }
20403         },
20404
20405         /**
20406          * Recursively searches the immediate parent and all child nodes for
20407          * the handle element in order to determine wheter or not it was
20408          * clicked.
20409          * @method handleWasClicked
20410          * @param node the html element to inspect
20411          * @static
20412          */
20413         handleWasClicked: function(node, id) {
20414             if (this.isHandle(id, node.id)) {
20415                 return true;
20416             } else {
20417                 // check to see if this is a text node child of the one we want
20418                 var p = node.parentNode;
20419
20420                 while (p) {
20421                     if (this.isHandle(id, p.id)) {
20422                         return true;
20423                     } else {
20424                         p = p.parentNode;
20425                     }
20426                 }
20427             }
20428
20429             return false;
20430         }
20431
20432     };
20433
20434 }();
20435
20436 // shorter alias, save a few bytes
20437 Roo.dd.DDM = Roo.dd.DragDropMgr;
20438 Roo.dd.DDM._addListeners();
20439
20440 }/*
20441  * Based on:
20442  * Ext JS Library 1.1.1
20443  * Copyright(c) 2006-2007, Ext JS, LLC.
20444  *
20445  * Originally Released Under LGPL - original licence link has changed is not relivant.
20446  *
20447  * Fork - LGPL
20448  * <script type="text/javascript">
20449  */
20450
20451 /**
20452  * @class Roo.dd.DD
20453  * A DragDrop implementation where the linked element follows the
20454  * mouse cursor during a drag.
20455  * @extends Roo.dd.DragDrop
20456  * @constructor
20457  * @param {String} id the id of the linked element
20458  * @param {String} sGroup the group of related DragDrop items
20459  * @param {object} config an object containing configurable attributes
20460  *                Valid properties for DD:
20461  *                    scroll
20462  */
20463 Roo.dd.DD = function(id, sGroup, config) {
20464     if (id) {
20465         this.init(id, sGroup, config);
20466     }
20467 };
20468
20469 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20470
20471     /**
20472      * When set to true, the utility automatically tries to scroll the browser
20473      * window wehn a drag and drop element is dragged near the viewport boundary.
20474      * Defaults to true.
20475      * @property scroll
20476      * @type boolean
20477      */
20478     scroll: true,
20479
20480     /**
20481      * Sets the pointer offset to the distance between the linked element's top
20482      * left corner and the location the element was clicked
20483      * @method autoOffset
20484      * @param {int} iPageX the X coordinate of the click
20485      * @param {int} iPageY the Y coordinate of the click
20486      */
20487     autoOffset: function(iPageX, iPageY) {
20488         var x = iPageX - this.startPageX;
20489         var y = iPageY - this.startPageY;
20490         this.setDelta(x, y);
20491     },
20492
20493     /**
20494      * Sets the pointer offset.  You can call this directly to force the
20495      * offset to be in a particular location (e.g., pass in 0,0 to set it
20496      * to the center of the object)
20497      * @method setDelta
20498      * @param {int} iDeltaX the distance from the left
20499      * @param {int} iDeltaY the distance from the top
20500      */
20501     setDelta: function(iDeltaX, iDeltaY) {
20502         this.deltaX = iDeltaX;
20503         this.deltaY = iDeltaY;
20504     },
20505
20506     /**
20507      * Sets the drag element to the location of the mousedown or click event,
20508      * maintaining the cursor location relative to the location on the element
20509      * that was clicked.  Override this if you want to place the element in a
20510      * location other than where the cursor is.
20511      * @method setDragElPos
20512      * @param {int} iPageX the X coordinate of the mousedown or drag event
20513      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20514      */
20515     setDragElPos: function(iPageX, iPageY) {
20516         // the first time we do this, we are going to check to make sure
20517         // the element has css positioning
20518
20519         var el = this.getDragEl();
20520         this.alignElWithMouse(el, iPageX, iPageY);
20521     },
20522
20523     /**
20524      * Sets the element to the location of the mousedown or click event,
20525      * maintaining the cursor location relative to the location on the element
20526      * that was clicked.  Override this if you want to place the element in a
20527      * location other than where the cursor is.
20528      * @method alignElWithMouse
20529      * @param {HTMLElement} el the element to move
20530      * @param {int} iPageX the X coordinate of the mousedown or drag event
20531      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20532      */
20533     alignElWithMouse: function(el, iPageX, iPageY) {
20534         var oCoord = this.getTargetCoord(iPageX, iPageY);
20535         var fly = el.dom ? el : Roo.fly(el);
20536         if (!this.deltaSetXY) {
20537             var aCoord = [oCoord.x, oCoord.y];
20538             fly.setXY(aCoord);
20539             var newLeft = fly.getLeft(true);
20540             var newTop  = fly.getTop(true);
20541             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20542         } else {
20543             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20544         }
20545
20546         this.cachePosition(oCoord.x, oCoord.y);
20547         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20548         return oCoord;
20549     },
20550
20551     /**
20552      * Saves the most recent position so that we can reset the constraints and
20553      * tick marks on-demand.  We need to know this so that we can calculate the
20554      * number of pixels the element is offset from its original position.
20555      * @method cachePosition
20556      * @param iPageX the current x position (optional, this just makes it so we
20557      * don't have to look it up again)
20558      * @param iPageY the current y position (optional, this just makes it so we
20559      * don't have to look it up again)
20560      */
20561     cachePosition: function(iPageX, iPageY) {
20562         if (iPageX) {
20563             this.lastPageX = iPageX;
20564             this.lastPageY = iPageY;
20565         } else {
20566             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20567             this.lastPageX = aCoord[0];
20568             this.lastPageY = aCoord[1];
20569         }
20570     },
20571
20572     /**
20573      * Auto-scroll the window if the dragged object has been moved beyond the
20574      * visible window boundary.
20575      * @method autoScroll
20576      * @param {int} x the drag element's x position
20577      * @param {int} y the drag element's y position
20578      * @param {int} h the height of the drag element
20579      * @param {int} w the width of the drag element
20580      * @private
20581      */
20582     autoScroll: function(x, y, h, w) {
20583
20584         if (this.scroll) {
20585             // The client height
20586             var clientH = Roo.lib.Dom.getViewWidth();
20587
20588             // The client width
20589             var clientW = Roo.lib.Dom.getViewHeight();
20590
20591             // The amt scrolled down
20592             var st = this.DDM.getScrollTop();
20593
20594             // The amt scrolled right
20595             var sl = this.DDM.getScrollLeft();
20596
20597             // Location of the bottom of the element
20598             var bot = h + y;
20599
20600             // Location of the right of the element
20601             var right = w + x;
20602
20603             // The distance from the cursor to the bottom of the visible area,
20604             // adjusted so that we don't scroll if the cursor is beyond the
20605             // element drag constraints
20606             var toBot = (clientH + st - y - this.deltaY);
20607
20608             // The distance from the cursor to the right of the visible area
20609             var toRight = (clientW + sl - x - this.deltaX);
20610
20611
20612             // How close to the edge the cursor must be before we scroll
20613             // var thresh = (document.all) ? 100 : 40;
20614             var thresh = 40;
20615
20616             // How many pixels to scroll per autoscroll op.  This helps to reduce
20617             // clunky scrolling. IE is more sensitive about this ... it needs this
20618             // value to be higher.
20619             var scrAmt = (document.all) ? 80 : 30;
20620
20621             // Scroll down if we are near the bottom of the visible page and the
20622             // obj extends below the crease
20623             if ( bot > clientH && toBot < thresh ) {
20624                 window.scrollTo(sl, st + scrAmt);
20625             }
20626
20627             // Scroll up if the window is scrolled down and the top of the object
20628             // goes above the top border
20629             if ( y < st && st > 0 && y - st < thresh ) {
20630                 window.scrollTo(sl, st - scrAmt);
20631             }
20632
20633             // Scroll right if the obj is beyond the right border and the cursor is
20634             // near the border.
20635             if ( right > clientW && toRight < thresh ) {
20636                 window.scrollTo(sl + scrAmt, st);
20637             }
20638
20639             // Scroll left if the window has been scrolled to the right and the obj
20640             // extends past the left border
20641             if ( x < sl && sl > 0 && x - sl < thresh ) {
20642                 window.scrollTo(sl - scrAmt, st);
20643             }
20644         }
20645     },
20646
20647     /**
20648      * Finds the location the element should be placed if we want to move
20649      * it to where the mouse location less the click offset would place us.
20650      * @method getTargetCoord
20651      * @param {int} iPageX the X coordinate of the click
20652      * @param {int} iPageY the Y coordinate of the click
20653      * @return an object that contains the coordinates (Object.x and Object.y)
20654      * @private
20655      */
20656     getTargetCoord: function(iPageX, iPageY) {
20657
20658
20659         var x = iPageX - this.deltaX;
20660         var y = iPageY - this.deltaY;
20661
20662         if (this.constrainX) {
20663             if (x < this.minX) { x = this.minX; }
20664             if (x > this.maxX) { x = this.maxX; }
20665         }
20666
20667         if (this.constrainY) {
20668             if (y < this.minY) { y = this.minY; }
20669             if (y > this.maxY) { y = this.maxY; }
20670         }
20671
20672         x = this.getTick(x, this.xTicks);
20673         y = this.getTick(y, this.yTicks);
20674
20675
20676         return {x:x, y:y};
20677     },
20678
20679     /*
20680      * Sets up config options specific to this class. Overrides
20681      * Roo.dd.DragDrop, but all versions of this method through the
20682      * inheritance chain are called
20683      */
20684     applyConfig: function() {
20685         Roo.dd.DD.superclass.applyConfig.call(this);
20686         this.scroll = (this.config.scroll !== false);
20687     },
20688
20689     /*
20690      * Event that fires prior to the onMouseDown event.  Overrides
20691      * Roo.dd.DragDrop.
20692      */
20693     b4MouseDown: function(e) {
20694         // this.resetConstraints();
20695         this.autoOffset(e.getPageX(),
20696                             e.getPageY());
20697     },
20698
20699     /*
20700      * Event that fires prior to the onDrag event.  Overrides
20701      * Roo.dd.DragDrop.
20702      */
20703     b4Drag: function(e) {
20704         this.setDragElPos(e.getPageX(),
20705                             e.getPageY());
20706     },
20707
20708     toString: function() {
20709         return ("DD " + this.id);
20710     }
20711
20712     //////////////////////////////////////////////////////////////////////////
20713     // Debugging ygDragDrop events that can be overridden
20714     //////////////////////////////////////////////////////////////////////////
20715     /*
20716     startDrag: function(x, y) {
20717     },
20718
20719     onDrag: function(e) {
20720     },
20721
20722     onDragEnter: function(e, id) {
20723     },
20724
20725     onDragOver: function(e, id) {
20726     },
20727
20728     onDragOut: function(e, id) {
20729     },
20730
20731     onDragDrop: function(e, id) {
20732     },
20733
20734     endDrag: function(e) {
20735     }
20736
20737     */
20738
20739 });/*
20740  * Based on:
20741  * Ext JS Library 1.1.1
20742  * Copyright(c) 2006-2007, Ext JS, LLC.
20743  *
20744  * Originally Released Under LGPL - original licence link has changed is not relivant.
20745  *
20746  * Fork - LGPL
20747  * <script type="text/javascript">
20748  */
20749
20750 /**
20751  * @class Roo.dd.DDProxy
20752  * A DragDrop implementation that inserts an empty, bordered div into
20753  * the document that follows the cursor during drag operations.  At the time of
20754  * the click, the frame div is resized to the dimensions of the linked html
20755  * element, and moved to the exact location of the linked element.
20756  *
20757  * References to the "frame" element refer to the single proxy element that
20758  * was created to be dragged in place of all DDProxy elements on the
20759  * page.
20760  *
20761  * @extends Roo.dd.DD
20762  * @constructor
20763  * @param {String} id the id of the linked html element
20764  * @param {String} sGroup the group of related DragDrop objects
20765  * @param {object} config an object containing configurable attributes
20766  *                Valid properties for DDProxy in addition to those in DragDrop:
20767  *                   resizeFrame, centerFrame, dragElId
20768  */
20769 Roo.dd.DDProxy = function(id, sGroup, config) {
20770     if (id) {
20771         this.init(id, sGroup, config);
20772         this.initFrame();
20773     }
20774 };
20775
20776 /**
20777  * The default drag frame div id
20778  * @property Roo.dd.DDProxy.dragElId
20779  * @type String
20780  * @static
20781  */
20782 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20783
20784 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20785
20786     /**
20787      * By default we resize the drag frame to be the same size as the element
20788      * we want to drag (this is to get the frame effect).  We can turn it off
20789      * if we want a different behavior.
20790      * @property resizeFrame
20791      * @type boolean
20792      */
20793     resizeFrame: true,
20794
20795     /**
20796      * By default the frame is positioned exactly where the drag element is, so
20797      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20798      * you do not have constraints on the obj is to have the drag frame centered
20799      * around the cursor.  Set centerFrame to true for this effect.
20800      * @property centerFrame
20801      * @type boolean
20802      */
20803     centerFrame: false,
20804
20805     /**
20806      * Creates the proxy element if it does not yet exist
20807      * @method createFrame
20808      */
20809     createFrame: function() {
20810         var self = this;
20811         var body = document.body;
20812
20813         if (!body || !body.firstChild) {
20814             setTimeout( function() { self.createFrame(); }, 50 );
20815             return;
20816         }
20817
20818         var div = this.getDragEl();
20819
20820         if (!div) {
20821             div    = document.createElement("div");
20822             div.id = this.dragElId;
20823             var s  = div.style;
20824
20825             s.position   = "absolute";
20826             s.visibility = "hidden";
20827             s.cursor     = "move";
20828             s.border     = "2px solid #aaa";
20829             s.zIndex     = 999;
20830
20831             // appendChild can blow up IE if invoked prior to the window load event
20832             // while rendering a table.  It is possible there are other scenarios
20833             // that would cause this to happen as well.
20834             body.insertBefore(div, body.firstChild);
20835         }
20836     },
20837
20838     /**
20839      * Initialization for the drag frame element.  Must be called in the
20840      * constructor of all subclasses
20841      * @method initFrame
20842      */
20843     initFrame: function() {
20844         this.createFrame();
20845     },
20846
20847     applyConfig: function() {
20848         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20849
20850         this.resizeFrame = (this.config.resizeFrame !== false);
20851         this.centerFrame = (this.config.centerFrame);
20852         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20853     },
20854
20855     /**
20856      * Resizes the drag frame to the dimensions of the clicked object, positions
20857      * it over the object, and finally displays it
20858      * @method showFrame
20859      * @param {int} iPageX X click position
20860      * @param {int} iPageY Y click position
20861      * @private
20862      */
20863     showFrame: function(iPageX, iPageY) {
20864         var el = this.getEl();
20865         var dragEl = this.getDragEl();
20866         var s = dragEl.style;
20867
20868         this._resizeProxy();
20869
20870         if (this.centerFrame) {
20871             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20872                            Math.round(parseInt(s.height, 10)/2) );
20873         }
20874
20875         this.setDragElPos(iPageX, iPageY);
20876
20877         Roo.fly(dragEl).show();
20878     },
20879
20880     /**
20881      * The proxy is automatically resized to the dimensions of the linked
20882      * element when a drag is initiated, unless resizeFrame is set to false
20883      * @method _resizeProxy
20884      * @private
20885      */
20886     _resizeProxy: function() {
20887         if (this.resizeFrame) {
20888             var el = this.getEl();
20889             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20890         }
20891     },
20892
20893     // overrides Roo.dd.DragDrop
20894     b4MouseDown: function(e) {
20895         var x = e.getPageX();
20896         var y = e.getPageY();
20897         this.autoOffset(x, y);
20898         this.setDragElPos(x, y);
20899     },
20900
20901     // overrides Roo.dd.DragDrop
20902     b4StartDrag: function(x, y) {
20903         // show the drag frame
20904         this.showFrame(x, y);
20905     },
20906
20907     // overrides Roo.dd.DragDrop
20908     b4EndDrag: function(e) {
20909         Roo.fly(this.getDragEl()).hide();
20910     },
20911
20912     // overrides Roo.dd.DragDrop
20913     // By default we try to move the element to the last location of the frame.
20914     // This is so that the default behavior mirrors that of Roo.dd.DD.
20915     endDrag: function(e) {
20916
20917         var lel = this.getEl();
20918         var del = this.getDragEl();
20919
20920         // Show the drag frame briefly so we can get its position
20921         del.style.visibility = "";
20922
20923         this.beforeMove();
20924         // Hide the linked element before the move to get around a Safari
20925         // rendering bug.
20926         lel.style.visibility = "hidden";
20927         Roo.dd.DDM.moveToEl(lel, del);
20928         del.style.visibility = "hidden";
20929         lel.style.visibility = "";
20930
20931         this.afterDrag();
20932     },
20933
20934     beforeMove : function(){
20935
20936     },
20937
20938     afterDrag : function(){
20939
20940     },
20941
20942     toString: function() {
20943         return ("DDProxy " + this.id);
20944     }
20945
20946 });
20947 /*
20948  * Based on:
20949  * Ext JS Library 1.1.1
20950  * Copyright(c) 2006-2007, Ext JS, LLC.
20951  *
20952  * Originally Released Under LGPL - original licence link has changed is not relivant.
20953  *
20954  * Fork - LGPL
20955  * <script type="text/javascript">
20956  */
20957
20958  /**
20959  * @class Roo.dd.DDTarget
20960  * A DragDrop implementation that does not move, but can be a drop
20961  * target.  You would get the same result by simply omitting implementation
20962  * for the event callbacks, but this way we reduce the processing cost of the
20963  * event listener and the callbacks.
20964  * @extends Roo.dd.DragDrop
20965  * @constructor
20966  * @param {String} id the id of the element that is a drop target
20967  * @param {String} sGroup the group of related DragDrop objects
20968  * @param {object} config an object containing configurable attributes
20969  *                 Valid properties for DDTarget in addition to those in
20970  *                 DragDrop:
20971  *                    none
20972  */
20973 Roo.dd.DDTarget = function(id, sGroup, config) {
20974     if (id) {
20975         this.initTarget(id, sGroup, config);
20976     }
20977     if (config.listeners || config.events) { 
20978        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20979             listeners : config.listeners || {}, 
20980             events : config.events || {} 
20981         });    
20982     }
20983 };
20984
20985 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20986 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20987     toString: function() {
20988         return ("DDTarget " + this.id);
20989     }
20990 });
20991 /*
20992  * Based on:
20993  * Ext JS Library 1.1.1
20994  * Copyright(c) 2006-2007, Ext JS, LLC.
20995  *
20996  * Originally Released Under LGPL - original licence link has changed is not relivant.
20997  *
20998  * Fork - LGPL
20999  * <script type="text/javascript">
21000  */
21001  
21002
21003 /**
21004  * @class Roo.dd.ScrollManager
21005  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21006  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21007  * @singleton
21008  */
21009 Roo.dd.ScrollManager = function(){
21010     var ddm = Roo.dd.DragDropMgr;
21011     var els = {};
21012     var dragEl = null;
21013     var proc = {};
21014     
21015     
21016     
21017     var onStop = function(e){
21018         dragEl = null;
21019         clearProc();
21020     };
21021     
21022     var triggerRefresh = function(){
21023         if(ddm.dragCurrent){
21024              ddm.refreshCache(ddm.dragCurrent.groups);
21025         }
21026     };
21027     
21028     var doScroll = function(){
21029         if(ddm.dragCurrent){
21030             var dds = Roo.dd.ScrollManager;
21031             if(!dds.animate){
21032                 if(proc.el.scroll(proc.dir, dds.increment)){
21033                     triggerRefresh();
21034                 }
21035             }else{
21036                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21037             }
21038         }
21039     };
21040     
21041     var clearProc = function(){
21042         if(proc.id){
21043             clearInterval(proc.id);
21044         }
21045         proc.id = 0;
21046         proc.el = null;
21047         proc.dir = "";
21048     };
21049     
21050     var startProc = function(el, dir){
21051          Roo.log('scroll startproc');
21052         clearProc();
21053         proc.el = el;
21054         proc.dir = dir;
21055         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21056     };
21057     
21058     var onFire = function(e, isDrop){
21059        
21060         if(isDrop || !ddm.dragCurrent){ return; }
21061         var dds = Roo.dd.ScrollManager;
21062         if(!dragEl || dragEl != ddm.dragCurrent){
21063             dragEl = ddm.dragCurrent;
21064             // refresh regions on drag start
21065             dds.refreshCache();
21066         }
21067         
21068         var xy = Roo.lib.Event.getXY(e);
21069         var pt = new Roo.lib.Point(xy[0], xy[1]);
21070         for(var id in els){
21071             var el = els[id], r = el._region;
21072             if(r && r.contains(pt) && el.isScrollable()){
21073                 if(r.bottom - pt.y <= dds.thresh){
21074                     if(proc.el != el){
21075                         startProc(el, "down");
21076                     }
21077                     return;
21078                 }else if(r.right - pt.x <= dds.thresh){
21079                     if(proc.el != el){
21080                         startProc(el, "left");
21081                     }
21082                     return;
21083                 }else if(pt.y - r.top <= dds.thresh){
21084                     if(proc.el != el){
21085                         startProc(el, "up");
21086                     }
21087                     return;
21088                 }else if(pt.x - r.left <= dds.thresh){
21089                     if(proc.el != el){
21090                         startProc(el, "right");
21091                     }
21092                     return;
21093                 }
21094             }
21095         }
21096         clearProc();
21097     };
21098     
21099     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21100     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21101     
21102     return {
21103         /**
21104          * Registers new overflow element(s) to auto scroll
21105          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21106          */
21107         register : function(el){
21108             if(el instanceof Array){
21109                 for(var i = 0, len = el.length; i < len; i++) {
21110                         this.register(el[i]);
21111                 }
21112             }else{
21113                 el = Roo.get(el);
21114                 els[el.id] = el;
21115             }
21116             Roo.dd.ScrollManager.els = els;
21117         },
21118         
21119         /**
21120          * Unregisters overflow element(s) so they are no longer scrolled
21121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21122          */
21123         unregister : function(el){
21124             if(el instanceof Array){
21125                 for(var i = 0, len = el.length; i < len; i++) {
21126                         this.unregister(el[i]);
21127                 }
21128             }else{
21129                 el = Roo.get(el);
21130                 delete els[el.id];
21131             }
21132         },
21133         
21134         /**
21135          * The number of pixels from the edge of a container the pointer needs to be to 
21136          * trigger scrolling (defaults to 25)
21137          * @type Number
21138          */
21139         thresh : 25,
21140         
21141         /**
21142          * The number of pixels to scroll in each scroll increment (defaults to 50)
21143          * @type Number
21144          */
21145         increment : 100,
21146         
21147         /**
21148          * The frequency of scrolls in milliseconds (defaults to 500)
21149          * @type Number
21150          */
21151         frequency : 500,
21152         
21153         /**
21154          * True to animate the scroll (defaults to true)
21155          * @type Boolean
21156          */
21157         animate: true,
21158         
21159         /**
21160          * The animation duration in seconds - 
21161          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21162          * @type Number
21163          */
21164         animDuration: .4,
21165         
21166         /**
21167          * Manually trigger a cache refresh.
21168          */
21169         refreshCache : function(){
21170             for(var id in els){
21171                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21172                     els[id]._region = els[id].getRegion();
21173                 }
21174             }
21175         }
21176     };
21177 }();/*
21178  * Based on:
21179  * Ext JS Library 1.1.1
21180  * Copyright(c) 2006-2007, Ext JS, LLC.
21181  *
21182  * Originally Released Under LGPL - original licence link has changed is not relivant.
21183  *
21184  * Fork - LGPL
21185  * <script type="text/javascript">
21186  */
21187  
21188
21189 /**
21190  * @class Roo.dd.Registry
21191  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21192  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21193  * @singleton
21194  */
21195 Roo.dd.Registry = function(){
21196     var elements = {}; 
21197     var handles = {}; 
21198     var autoIdSeed = 0;
21199
21200     var getId = function(el, autogen){
21201         if(typeof el == "string"){
21202             return el;
21203         }
21204         var id = el.id;
21205         if(!id && autogen !== false){
21206             id = "roodd-" + (++autoIdSeed);
21207             el.id = id;
21208         }
21209         return id;
21210     };
21211     
21212     return {
21213     /**
21214      * Register a drag drop element
21215      * @param {String|HTMLElement} element The id or DOM node to register
21216      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21217      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21218      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21219      * populated in the data object (if applicable):
21220      * <pre>
21221 Value      Description<br />
21222 ---------  ------------------------------------------<br />
21223 handles    Array of DOM nodes that trigger dragging<br />
21224            for the element being registered<br />
21225 isHandle   True if the element passed in triggers<br />
21226            dragging itself, else false
21227 </pre>
21228      */
21229         register : function(el, data){
21230             data = data || {};
21231             if(typeof el == "string"){
21232                 el = document.getElementById(el);
21233             }
21234             data.ddel = el;
21235             elements[getId(el)] = data;
21236             if(data.isHandle !== false){
21237                 handles[data.ddel.id] = data;
21238             }
21239             if(data.handles){
21240                 var hs = data.handles;
21241                 for(var i = 0, len = hs.length; i < len; i++){
21242                         handles[getId(hs[i])] = data;
21243                 }
21244             }
21245         },
21246
21247     /**
21248      * Unregister a drag drop element
21249      * @param {String|HTMLElement}  element The id or DOM node to unregister
21250      */
21251         unregister : function(el){
21252             var id = getId(el, false);
21253             var data = elements[id];
21254             if(data){
21255                 delete elements[id];
21256                 if(data.handles){
21257                     var hs = data.handles;
21258                     for(var i = 0, len = hs.length; i < len; i++){
21259                         delete handles[getId(hs[i], false)];
21260                     }
21261                 }
21262             }
21263         },
21264
21265     /**
21266      * Returns the handle registered for a DOM Node by id
21267      * @param {String|HTMLElement} id The DOM node or id to look up
21268      * @return {Object} handle The custom handle data
21269      */
21270         getHandle : function(id){
21271             if(typeof id != "string"){ // must be element?
21272                 id = id.id;
21273             }
21274             return handles[id];
21275         },
21276
21277     /**
21278      * Returns the handle that is registered for the DOM node that is the target of the event
21279      * @param {Event} e The event
21280      * @return {Object} handle The custom handle data
21281      */
21282         getHandleFromEvent : function(e){
21283             var t = Roo.lib.Event.getTarget(e);
21284             return t ? handles[t.id] : null;
21285         },
21286
21287     /**
21288      * Returns a custom data object that is registered for a DOM node by id
21289      * @param {String|HTMLElement} id The DOM node or id to look up
21290      * @return {Object} data The custom data
21291      */
21292         getTarget : function(id){
21293             if(typeof id != "string"){ // must be element?
21294                 id = id.id;
21295             }
21296             return elements[id];
21297         },
21298
21299     /**
21300      * Returns a custom data object that is registered for the DOM node that is the target of the event
21301      * @param {Event} e The event
21302      * @return {Object} data The custom data
21303      */
21304         getTargetFromEvent : function(e){
21305             var t = Roo.lib.Event.getTarget(e);
21306             return t ? elements[t.id] || handles[t.id] : null;
21307         }
21308     };
21309 }();/*
21310  * Based on:
21311  * Ext JS Library 1.1.1
21312  * Copyright(c) 2006-2007, Ext JS, LLC.
21313  *
21314  * Originally Released Under LGPL - original licence link has changed is not relivant.
21315  *
21316  * Fork - LGPL
21317  * <script type="text/javascript">
21318  */
21319  
21320
21321 /**
21322  * @class Roo.dd.StatusProxy
21323  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21324  * default drag proxy used by all Roo.dd components.
21325  * @constructor
21326  * @param {Object} config
21327  */
21328 Roo.dd.StatusProxy = function(config){
21329     Roo.apply(this, config);
21330     this.id = this.id || Roo.id();
21331     this.el = new Roo.Layer({
21332         dh: {
21333             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21334                 {tag: "div", cls: "x-dd-drop-icon"},
21335                 {tag: "div", cls: "x-dd-drag-ghost"}
21336             ]
21337         }, 
21338         shadow: !config || config.shadow !== false
21339     });
21340     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21341     this.dropStatus = this.dropNotAllowed;
21342 };
21343
21344 Roo.dd.StatusProxy.prototype = {
21345     /**
21346      * @cfg {String} dropAllowed
21347      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21348      */
21349     dropAllowed : "x-dd-drop-ok",
21350     /**
21351      * @cfg {String} dropNotAllowed
21352      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21353      */
21354     dropNotAllowed : "x-dd-drop-nodrop",
21355
21356     /**
21357      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21358      * over the current target element.
21359      * @param {String} cssClass The css class for the new drop status indicator image
21360      */
21361     setStatus : function(cssClass){
21362         cssClass = cssClass || this.dropNotAllowed;
21363         if(this.dropStatus != cssClass){
21364             this.el.replaceClass(this.dropStatus, cssClass);
21365             this.dropStatus = cssClass;
21366         }
21367     },
21368
21369     /**
21370      * Resets the status indicator to the default dropNotAllowed value
21371      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21372      */
21373     reset : function(clearGhost){
21374         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21375         this.dropStatus = this.dropNotAllowed;
21376         if(clearGhost){
21377             this.ghost.update("");
21378         }
21379     },
21380
21381     /**
21382      * Updates the contents of the ghost element
21383      * @param {String} html The html that will replace the current innerHTML of the ghost element
21384      */
21385     update : function(html){
21386         if(typeof html == "string"){
21387             this.ghost.update(html);
21388         }else{
21389             this.ghost.update("");
21390             html.style.margin = "0";
21391             this.ghost.dom.appendChild(html);
21392         }
21393         // ensure float = none set?? cant remember why though.
21394         var el = this.ghost.dom.firstChild;
21395                 if(el){
21396                         Roo.fly(el).setStyle('float', 'none');
21397                 }
21398     },
21399     
21400     /**
21401      * Returns the underlying proxy {@link Roo.Layer}
21402      * @return {Roo.Layer} el
21403     */
21404     getEl : function(){
21405         return this.el;
21406     },
21407
21408     /**
21409      * Returns the ghost element
21410      * @return {Roo.Element} el
21411      */
21412     getGhost : function(){
21413         return this.ghost;
21414     },
21415
21416     /**
21417      * Hides the proxy
21418      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21419      */
21420     hide : function(clear){
21421         this.el.hide();
21422         if(clear){
21423             this.reset(true);
21424         }
21425     },
21426
21427     /**
21428      * Stops the repair animation if it's currently running
21429      */
21430     stop : function(){
21431         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21432             this.anim.stop();
21433         }
21434     },
21435
21436     /**
21437      * Displays this proxy
21438      */
21439     show : function(){
21440         this.el.show();
21441     },
21442
21443     /**
21444      * Force the Layer to sync its shadow and shim positions to the element
21445      */
21446     sync : function(){
21447         this.el.sync();
21448     },
21449
21450     /**
21451      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21452      * invalid drop operation by the item being dragged.
21453      * @param {Array} xy The XY position of the element ([x, y])
21454      * @param {Function} callback The function to call after the repair is complete
21455      * @param {Object} scope The scope in which to execute the callback
21456      */
21457     repair : function(xy, callback, scope){
21458         this.callback = callback;
21459         this.scope = scope;
21460         if(xy && this.animRepair !== false){
21461             this.el.addClass("x-dd-drag-repair");
21462             this.el.hideUnders(true);
21463             this.anim = this.el.shift({
21464                 duration: this.repairDuration || .5,
21465                 easing: 'easeOut',
21466                 xy: xy,
21467                 stopFx: true,
21468                 callback: this.afterRepair,
21469                 scope: this
21470             });
21471         }else{
21472             this.afterRepair();
21473         }
21474     },
21475
21476     // private
21477     afterRepair : function(){
21478         this.hide(true);
21479         if(typeof this.callback == "function"){
21480             this.callback.call(this.scope || this);
21481         }
21482         this.callback = null;
21483         this.scope = null;
21484     }
21485 };/*
21486  * Based on:
21487  * Ext JS Library 1.1.1
21488  * Copyright(c) 2006-2007, Ext JS, LLC.
21489  *
21490  * Originally Released Under LGPL - original licence link has changed is not relivant.
21491  *
21492  * Fork - LGPL
21493  * <script type="text/javascript">
21494  */
21495
21496 /**
21497  * @class Roo.dd.DragSource
21498  * @extends Roo.dd.DDProxy
21499  * A simple class that provides the basic implementation needed to make any element draggable.
21500  * @constructor
21501  * @param {String/HTMLElement/Element} el The container element
21502  * @param {Object} config
21503  */
21504 Roo.dd.DragSource = function(el, config){
21505     this.el = Roo.get(el);
21506     this.dragData = {};
21507     
21508     Roo.apply(this, config);
21509     
21510     if(!this.proxy){
21511         this.proxy = new Roo.dd.StatusProxy();
21512     }
21513
21514     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21515           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21516     
21517     this.dragging = false;
21518 };
21519
21520 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21521     /**
21522      * @cfg {String} dropAllowed
21523      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21524      */
21525     dropAllowed : "x-dd-drop-ok",
21526     /**
21527      * @cfg {String} dropNotAllowed
21528      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21529      */
21530     dropNotAllowed : "x-dd-drop-nodrop",
21531
21532     /**
21533      * Returns the data object associated with this drag source
21534      * @return {Object} data An object containing arbitrary data
21535      */
21536     getDragData : function(e){
21537         return this.dragData;
21538     },
21539
21540     // private
21541     onDragEnter : function(e, id){
21542         var target = Roo.dd.DragDropMgr.getDDById(id);
21543         this.cachedTarget = target;
21544         if(this.beforeDragEnter(target, e, id) !== false){
21545             if(target.isNotifyTarget){
21546                 var status = target.notifyEnter(this, e, this.dragData);
21547                 this.proxy.setStatus(status);
21548             }else{
21549                 this.proxy.setStatus(this.dropAllowed);
21550             }
21551             
21552             if(this.afterDragEnter){
21553                 /**
21554                  * An empty function by default, but provided so that you can perform a custom action
21555                  * when the dragged item enters the drop target by providing an implementation.
21556                  * @param {Roo.dd.DragDrop} target The drop target
21557                  * @param {Event} e The event object
21558                  * @param {String} id The id of the dragged element
21559                  * @method afterDragEnter
21560                  */
21561                 this.afterDragEnter(target, e, id);
21562             }
21563         }
21564     },
21565
21566     /**
21567      * An empty function by default, but provided so that you can perform a custom action
21568      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21569      * @param {Roo.dd.DragDrop} target The drop target
21570      * @param {Event} e The event object
21571      * @param {String} id The id of the dragged element
21572      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21573      */
21574     beforeDragEnter : function(target, e, id){
21575         return true;
21576     },
21577
21578     // private
21579     alignElWithMouse: function() {
21580         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21581         this.proxy.sync();
21582     },
21583
21584     // private
21585     onDragOver : function(e, id){
21586         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21587         if(this.beforeDragOver(target, e, id) !== false){
21588             if(target.isNotifyTarget){
21589                 var status = target.notifyOver(this, e, this.dragData);
21590                 this.proxy.setStatus(status);
21591             }
21592
21593             if(this.afterDragOver){
21594                 /**
21595                  * An empty function by default, but provided so that you can perform a custom action
21596                  * while the dragged item is over the drop target by providing an implementation.
21597                  * @param {Roo.dd.DragDrop} target The drop target
21598                  * @param {Event} e The event object
21599                  * @param {String} id The id of the dragged element
21600                  * @method afterDragOver
21601                  */
21602                 this.afterDragOver(target, e, id);
21603             }
21604         }
21605     },
21606
21607     /**
21608      * An empty function by default, but provided so that you can perform a custom action
21609      * while the dragged item is over the drop target and optionally cancel the onDragOver.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21614      */
21615     beforeDragOver : function(target, e, id){
21616         return true;
21617     },
21618
21619     // private
21620     onDragOut : function(e, id){
21621         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21622         if(this.beforeDragOut(target, e, id) !== false){
21623             if(target.isNotifyTarget){
21624                 target.notifyOut(this, e, this.dragData);
21625             }
21626             this.proxy.reset();
21627             if(this.afterDragOut){
21628                 /**
21629                  * An empty function by default, but provided so that you can perform a custom action
21630                  * after the dragged item is dragged out of the target without dropping.
21631                  * @param {Roo.dd.DragDrop} target The drop target
21632                  * @param {Event} e The event object
21633                  * @param {String} id The id of the dragged element
21634                  * @method afterDragOut
21635                  */
21636                 this.afterDragOut(target, e, id);
21637             }
21638         }
21639         this.cachedTarget = null;
21640     },
21641
21642     /**
21643      * An empty function by default, but provided so that you can perform a custom action before the dragged
21644      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21645      * @param {Roo.dd.DragDrop} target The drop target
21646      * @param {Event} e The event object
21647      * @param {String} id The id of the dragged element
21648      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21649      */
21650     beforeDragOut : function(target, e, id){
21651         return true;
21652     },
21653     
21654     // private
21655     onDragDrop : function(e, id){
21656         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21657         if(this.beforeDragDrop(target, e, id) !== false){
21658             if(target.isNotifyTarget){
21659                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21660                     this.onValidDrop(target, e, id);
21661                 }else{
21662                     this.onInvalidDrop(target, e, id);
21663                 }
21664             }else{
21665                 this.onValidDrop(target, e, id);
21666             }
21667             
21668             if(this.afterDragDrop){
21669                 /**
21670                  * An empty function by default, but provided so that you can perform a custom action
21671                  * after a valid drag drop has occurred by providing an implementation.
21672                  * @param {Roo.dd.DragDrop} target The drop target
21673                  * @param {Event} e The event object
21674                  * @param {String} id The id of the dropped element
21675                  * @method afterDragDrop
21676                  */
21677                 this.afterDragDrop(target, e, id);
21678             }
21679         }
21680         delete this.cachedTarget;
21681     },
21682
21683     /**
21684      * An empty function by default, but provided so that you can perform a custom action before the dragged
21685      * item is dropped onto the target and optionally cancel the onDragDrop.
21686      * @param {Roo.dd.DragDrop} target The drop target
21687      * @param {Event} e The event object
21688      * @param {String} id The id of the dragged element
21689      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21690      */
21691     beforeDragDrop : function(target, e, id){
21692         return true;
21693     },
21694
21695     // private
21696     onValidDrop : function(target, e, id){
21697         this.hideProxy();
21698         if(this.afterValidDrop){
21699             /**
21700              * An empty function by default, but provided so that you can perform a custom action
21701              * after a valid drop has occurred by providing an implementation.
21702              * @param {Object} target The target DD 
21703              * @param {Event} e The event object
21704              * @param {String} id The id of the dropped element
21705              * @method afterInvalidDrop
21706              */
21707             this.afterValidDrop(target, e, id);
21708         }
21709     },
21710
21711     // private
21712     getRepairXY : function(e, data){
21713         return this.el.getXY();  
21714     },
21715
21716     // private
21717     onInvalidDrop : function(target, e, id){
21718         this.beforeInvalidDrop(target, e, id);
21719         if(this.cachedTarget){
21720             if(this.cachedTarget.isNotifyTarget){
21721                 this.cachedTarget.notifyOut(this, e, this.dragData);
21722             }
21723             this.cacheTarget = null;
21724         }
21725         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21726
21727         if(this.afterInvalidDrop){
21728             /**
21729              * An empty function by default, but provided so that you can perform a custom action
21730              * after an invalid drop has occurred by providing an implementation.
21731              * @param {Event} e The event object
21732              * @param {String} id The id of the dropped element
21733              * @method afterInvalidDrop
21734              */
21735             this.afterInvalidDrop(e, id);
21736         }
21737     },
21738
21739     // private
21740     afterRepair : function(){
21741         if(Roo.enableFx){
21742             this.el.highlight(this.hlColor || "c3daf9");
21743         }
21744         this.dragging = false;
21745     },
21746
21747     /**
21748      * An empty function by default, but provided so that you can perform a custom action after an invalid
21749      * drop has occurred.
21750      * @param {Roo.dd.DragDrop} target The drop target
21751      * @param {Event} e The event object
21752      * @param {String} id The id of the dragged element
21753      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21754      */
21755     beforeInvalidDrop : function(target, e, id){
21756         return true;
21757     },
21758
21759     // private
21760     handleMouseDown : function(e){
21761         if(this.dragging) {
21762             return;
21763         }
21764         var data = this.getDragData(e);
21765         if(data && this.onBeforeDrag(data, e) !== false){
21766             this.dragData = data;
21767             this.proxy.stop();
21768             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21769         } 
21770     },
21771
21772     /**
21773      * An empty function by default, but provided so that you can perform a custom action before the initial
21774      * drag event begins and optionally cancel it.
21775      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21776      * @param {Event} e The event object
21777      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21778      */
21779     onBeforeDrag : function(data, e){
21780         return true;
21781     },
21782
21783     /**
21784      * An empty function by default, but provided so that you can perform a custom action once the initial
21785      * drag event has begun.  The drag cannot be canceled from this function.
21786      * @param {Number} x The x position of the click on the dragged object
21787      * @param {Number} y The y position of the click on the dragged object
21788      */
21789     onStartDrag : Roo.emptyFn,
21790
21791     // private - YUI override
21792     startDrag : function(x, y){
21793         this.proxy.reset();
21794         this.dragging = true;
21795         this.proxy.update("");
21796         this.onInitDrag(x, y);
21797         this.proxy.show();
21798     },
21799
21800     // private
21801     onInitDrag : function(x, y){
21802         var clone = this.el.dom.cloneNode(true);
21803         clone.id = Roo.id(); // prevent duplicate ids
21804         this.proxy.update(clone);
21805         this.onStartDrag(x, y);
21806         return true;
21807     },
21808
21809     /**
21810      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21811      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21812      */
21813     getProxy : function(){
21814         return this.proxy;  
21815     },
21816
21817     /**
21818      * Hides the drag source's {@link Roo.dd.StatusProxy}
21819      */
21820     hideProxy : function(){
21821         this.proxy.hide();  
21822         this.proxy.reset(true);
21823         this.dragging = false;
21824     },
21825
21826     // private
21827     triggerCacheRefresh : function(){
21828         Roo.dd.DDM.refreshCache(this.groups);
21829     },
21830
21831     // private - override to prevent hiding
21832     b4EndDrag: function(e) {
21833     },
21834
21835     // private - override to prevent moving
21836     endDrag : function(e){
21837         this.onEndDrag(this.dragData, e);
21838     },
21839
21840     // private
21841     onEndDrag : function(data, e){
21842     },
21843     
21844     // private - pin to cursor
21845     autoOffset : function(x, y) {
21846         this.setDelta(-12, -20);
21847     }    
21848 });/*
21849  * Based on:
21850  * Ext JS Library 1.1.1
21851  * Copyright(c) 2006-2007, Ext JS, LLC.
21852  *
21853  * Originally Released Under LGPL - original licence link has changed is not relivant.
21854  *
21855  * Fork - LGPL
21856  * <script type="text/javascript">
21857  */
21858
21859
21860 /**
21861  * @class Roo.dd.DropTarget
21862  * @extends Roo.dd.DDTarget
21863  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21864  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21865  * @constructor
21866  * @param {String/HTMLElement/Element} el The container element
21867  * @param {Object} config
21868  */
21869 Roo.dd.DropTarget = function(el, config){
21870     this.el = Roo.get(el);
21871     
21872     var listeners = false; ;
21873     if (config && config.listeners) {
21874         listeners= config.listeners;
21875         delete config.listeners;
21876     }
21877     Roo.apply(this, config);
21878     
21879     if(this.containerScroll){
21880         Roo.dd.ScrollManager.register(this.el);
21881     }
21882     this.addEvents( {
21883          /**
21884          * @scope Roo.dd.DropTarget
21885          */
21886          
21887          /**
21888          * @event enter
21889          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21890          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21891          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21892          * 
21893          * IMPORTANT : it should set this.overClass and this.dropAllowed
21894          * 
21895          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21896          * @param {Event} e The event
21897          * @param {Object} data An object containing arbitrary data supplied by the drag source
21898          */
21899         "enter" : true,
21900         
21901          /**
21902          * @event over
21903          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21904          * This method will be called on every mouse movement while the drag source is over the drop target.
21905          * This default implementation simply returns the dropAllowed config value.
21906          * 
21907          * IMPORTANT : it should set this.dropAllowed
21908          * 
21909          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21910          * @param {Event} e The event
21911          * @param {Object} data An object containing arbitrary data supplied by the drag source
21912          
21913          */
21914         "over" : true,
21915         /**
21916          * @event out
21917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21918          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21919          * overClass (if any) from the drop element.
21920          * 
21921          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21922          * @param {Event} e The event
21923          * @param {Object} data An object containing arbitrary data supplied by the drag source
21924          */
21925          "out" : true,
21926          
21927         /**
21928          * @event drop
21929          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21930          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21931          * implementation that does something to process the drop event and returns true so that the drag source's
21932          * repair action does not run.
21933          * 
21934          * IMPORTANT : it should set this.success
21935          * 
21936          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21937          * @param {Event} e The event
21938          * @param {Object} data An object containing arbitrary data supplied by the drag source
21939         */
21940          "drop" : true
21941     });
21942             
21943      
21944     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21945         this.el.dom, 
21946         this.ddGroup || this.group,
21947         {
21948             isTarget: true,
21949             listeners : listeners || {} 
21950            
21951         
21952         }
21953     );
21954
21955 };
21956
21957 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21958     /**
21959      * @cfg {String} overClass
21960      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21961      */
21962      /**
21963      * @cfg {String} ddGroup
21964      * The drag drop group to handle drop events for
21965      */
21966      
21967     /**
21968      * @cfg {String} dropAllowed
21969      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21970      */
21971     dropAllowed : "x-dd-drop-ok",
21972     /**
21973      * @cfg {String} dropNotAllowed
21974      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21975      */
21976     dropNotAllowed : "x-dd-drop-nodrop",
21977     /**
21978      * @cfg {boolean} success
21979      * set this after drop listener.. 
21980      */
21981     success : false,
21982     /**
21983      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21984      * if the drop point is valid for over/enter..
21985      */
21986     valid : false,
21987     // private
21988     isTarget : true,
21989
21990     // private
21991     isNotifyTarget : true,
21992     
21993     /**
21994      * @hide
21995      */
21996     notifyEnter : function(dd, e, data)
21997     {
21998         this.valid = true;
21999         this.fireEvent('enter', dd, e, data);
22000         if(this.overClass){
22001             this.el.addClass(this.overClass);
22002         }
22003         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22004             this.valid ? this.dropAllowed : this.dropNotAllowed
22005         );
22006     },
22007
22008     /**
22009      * @hide
22010      */
22011     notifyOver : function(dd, e, data)
22012     {
22013         this.valid = true;
22014         this.fireEvent('over', dd, e, data);
22015         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22016             this.valid ? this.dropAllowed : this.dropNotAllowed
22017         );
22018     },
22019
22020     /**
22021      * @hide
22022      */
22023     notifyOut : function(dd, e, data)
22024     {
22025         this.fireEvent('out', dd, e, data);
22026         if(this.overClass){
22027             this.el.removeClass(this.overClass);
22028         }
22029     },
22030
22031     /**
22032      * @hide
22033      */
22034     notifyDrop : function(dd, e, data)
22035     {
22036         this.success = false;
22037         this.fireEvent('drop', dd, e, data);
22038         return this.success;
22039     }
22040 });/*
22041  * Based on:
22042  * Ext JS Library 1.1.1
22043  * Copyright(c) 2006-2007, Ext JS, LLC.
22044  *
22045  * Originally Released Under LGPL - original licence link has changed is not relivant.
22046  *
22047  * Fork - LGPL
22048  * <script type="text/javascript">
22049  */
22050
22051
22052 /**
22053  * @class Roo.dd.DragZone
22054  * @extends Roo.dd.DragSource
22055  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22056  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22057  * @constructor
22058  * @param {String/HTMLElement/Element} el The container element
22059  * @param {Object} config
22060  */
22061 Roo.dd.DragZone = function(el, config){
22062     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22063     if(this.containerScroll){
22064         Roo.dd.ScrollManager.register(this.el);
22065     }
22066 };
22067
22068 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22069     /**
22070      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22071      * for auto scrolling during drag operations.
22072      */
22073     /**
22074      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22075      * method after a failed drop (defaults to "c3daf9" - light blue)
22076      */
22077
22078     /**
22079      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22080      * for a valid target to drag based on the mouse down. Override this method
22081      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22082      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22083      * @param {EventObject} e The mouse down event
22084      * @return {Object} The dragData
22085      */
22086     getDragData : function(e){
22087         return Roo.dd.Registry.getHandleFromEvent(e);
22088     },
22089     
22090     /**
22091      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22092      * this.dragData.ddel
22093      * @param {Number} x The x position of the click on the dragged object
22094      * @param {Number} y The y position of the click on the dragged object
22095      * @return {Boolean} true to continue the drag, false to cancel
22096      */
22097     onInitDrag : function(x, y){
22098         this.proxy.update(this.dragData.ddel.cloneNode(true));
22099         this.onStartDrag(x, y);
22100         return true;
22101     },
22102     
22103     /**
22104      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22105      */
22106     afterRepair : function(){
22107         if(Roo.enableFx){
22108             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22109         }
22110         this.dragging = false;
22111     },
22112
22113     /**
22114      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22115      * the XY of this.dragData.ddel
22116      * @param {EventObject} e The mouse up event
22117      * @return {Array} The xy location (e.g. [100, 200])
22118      */
22119     getRepairXY : function(e){
22120         return Roo.Element.fly(this.dragData.ddel).getXY();  
22121     }
22122 });/*
22123  * Based on:
22124  * Ext JS Library 1.1.1
22125  * Copyright(c) 2006-2007, Ext JS, LLC.
22126  *
22127  * Originally Released Under LGPL - original licence link has changed is not relivant.
22128  *
22129  * Fork - LGPL
22130  * <script type="text/javascript">
22131  */
22132 /**
22133  * @class Roo.dd.DropZone
22134  * @extends Roo.dd.DropTarget
22135  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22136  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22137  * @constructor
22138  * @param {String/HTMLElement/Element} el The container element
22139  * @param {Object} config
22140  */
22141 Roo.dd.DropZone = function(el, config){
22142     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22143 };
22144
22145 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22146     /**
22147      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22148      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22149      * provide your own custom lookup.
22150      * @param {Event} e The event
22151      * @return {Object} data The custom data
22152      */
22153     getTargetFromEvent : function(e){
22154         return Roo.dd.Registry.getTargetFromEvent(e);
22155     },
22156
22157     /**
22158      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22159      * that it has registered.  This method has no default implementation and should be overridden to provide
22160      * node-specific processing if necessary.
22161      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22162      * {@link #getTargetFromEvent} for this node)
22163      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22164      * @param {Event} e The event
22165      * @param {Object} data An object containing arbitrary data supplied by the drag source
22166      */
22167     onNodeEnter : function(n, dd, e, data){
22168         
22169     },
22170
22171     /**
22172      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22173      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22174      * overridden to provide the proper feedback.
22175      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22176      * {@link #getTargetFromEvent} for this node)
22177      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22178      * @param {Event} e The event
22179      * @param {Object} data An object containing arbitrary data supplied by the drag source
22180      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22181      * underlying {@link Roo.dd.StatusProxy} can be updated
22182      */
22183     onNodeOver : function(n, dd, e, data){
22184         return this.dropAllowed;
22185     },
22186
22187     /**
22188      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22189      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22190      * node-specific processing if necessary.
22191      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22192      * {@link #getTargetFromEvent} for this node)
22193      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22194      * @param {Event} e The event
22195      * @param {Object} data An object containing arbitrary data supplied by the drag source
22196      */
22197     onNodeOut : function(n, dd, e, data){
22198         
22199     },
22200
22201     /**
22202      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22203      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22204      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22205      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22206      * {@link #getTargetFromEvent} for this node)
22207      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22208      * @param {Event} e The event
22209      * @param {Object} data An object containing arbitrary data supplied by the drag source
22210      * @return {Boolean} True if the drop was valid, else false
22211      */
22212     onNodeDrop : function(n, dd, e, data){
22213         return false;
22214     },
22215
22216     /**
22217      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22218      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22219      * it should be overridden to provide the proper feedback if necessary.
22220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22221      * @param {Event} e The event
22222      * @param {Object} data An object containing arbitrary data supplied by the drag source
22223      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22224      * underlying {@link Roo.dd.StatusProxy} can be updated
22225      */
22226     onContainerOver : function(dd, e, data){
22227         return this.dropNotAllowed;
22228     },
22229
22230     /**
22231      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22232      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22233      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22234      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22236      * @param {Event} e The event
22237      * @param {Object} data An object containing arbitrary data supplied by the drag source
22238      * @return {Boolean} True if the drop was valid, else false
22239      */
22240     onContainerDrop : function(dd, e, data){
22241         return false;
22242     },
22243
22244     /**
22245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22246      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22247      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22248      * you should override this method and provide a custom implementation.
22249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22250      * @param {Event} e The event
22251      * @param {Object} data An object containing arbitrary data supplied by the drag source
22252      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22253      * underlying {@link Roo.dd.StatusProxy} can be updated
22254      */
22255     notifyEnter : function(dd, e, data){
22256         return this.dropNotAllowed;
22257     },
22258
22259     /**
22260      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22261      * This method will be called on every mouse movement while the drag source is over the drop zone.
22262      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22263      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22264      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22265      * registered node, it will call {@link #onContainerOver}.
22266      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22267      * @param {Event} e The event
22268      * @param {Object} data An object containing arbitrary data supplied by the drag source
22269      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22270      * underlying {@link Roo.dd.StatusProxy} can be updated
22271      */
22272     notifyOver : function(dd, e, data){
22273         var n = this.getTargetFromEvent(e);
22274         if(!n){ // not over valid drop target
22275             if(this.lastOverNode){
22276                 this.onNodeOut(this.lastOverNode, dd, e, data);
22277                 this.lastOverNode = null;
22278             }
22279             return this.onContainerOver(dd, e, data);
22280         }
22281         if(this.lastOverNode != n){
22282             if(this.lastOverNode){
22283                 this.onNodeOut(this.lastOverNode, dd, e, data);
22284             }
22285             this.onNodeEnter(n, dd, e, data);
22286             this.lastOverNode = n;
22287         }
22288         return this.onNodeOver(n, dd, e, data);
22289     },
22290
22291     /**
22292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22293      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22294      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22295      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22296      * @param {Event} e The event
22297      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22298      */
22299     notifyOut : function(dd, e, data){
22300         if(this.lastOverNode){
22301             this.onNodeOut(this.lastOverNode, dd, e, data);
22302             this.lastOverNode = null;
22303         }
22304     },
22305
22306     /**
22307      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22308      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22309      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22310      * otherwise it will call {@link #onContainerDrop}.
22311      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22312      * @param {Event} e The event
22313      * @param {Object} data An object containing arbitrary data supplied by the drag source
22314      * @return {Boolean} True if the drop was valid, else false
22315      */
22316     notifyDrop : function(dd, e, data){
22317         if(this.lastOverNode){
22318             this.onNodeOut(this.lastOverNode, dd, e, data);
22319             this.lastOverNode = null;
22320         }
22321         var n = this.getTargetFromEvent(e);
22322         return n ?
22323             this.onNodeDrop(n, dd, e, data) :
22324             this.onContainerDrop(dd, e, data);
22325     },
22326
22327     // private
22328     triggerCacheRefresh : function(){
22329         Roo.dd.DDM.refreshCache(this.groups);
22330     }  
22331 });/*
22332  * Based on:
22333  * Ext JS Library 1.1.1
22334  * Copyright(c) 2006-2007, Ext JS, LLC.
22335  *
22336  * Originally Released Under LGPL - original licence link has changed is not relivant.
22337  *
22338  * Fork - LGPL
22339  * <script type="text/javascript">
22340  */
22341
22342
22343 /**
22344  * @class Roo.data.SortTypes
22345  * @singleton
22346  * Defines the default sorting (casting?) comparison functions used when sorting data.
22347  */
22348 Roo.data.SortTypes = {
22349     /**
22350      * Default sort that does nothing
22351      * @param {Mixed} s The value being converted
22352      * @return {Mixed} The comparison value
22353      */
22354     none : function(s){
22355         return s;
22356     },
22357     
22358     /**
22359      * The regular expression used to strip tags
22360      * @type {RegExp}
22361      * @property
22362      */
22363     stripTagsRE : /<\/?[^>]+>/gi,
22364     
22365     /**
22366      * Strips all HTML tags to sort on text only
22367      * @param {Mixed} s The value being converted
22368      * @return {String} The comparison value
22369      */
22370     asText : function(s){
22371         return String(s).replace(this.stripTagsRE, "");
22372     },
22373     
22374     /**
22375      * Strips all HTML tags to sort on text only - Case insensitive
22376      * @param {Mixed} s The value being converted
22377      * @return {String} The comparison value
22378      */
22379     asUCText : function(s){
22380         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22381     },
22382     
22383     /**
22384      * Case insensitive string
22385      * @param {Mixed} s The value being converted
22386      * @return {String} The comparison value
22387      */
22388     asUCString : function(s) {
22389         return String(s).toUpperCase();
22390     },
22391     
22392     /**
22393      * Date sorting
22394      * @param {Mixed} s The value being converted
22395      * @return {Number} The comparison value
22396      */
22397     asDate : function(s) {
22398         if(!s){
22399             return 0;
22400         }
22401         if(s instanceof Date){
22402             return s.getTime();
22403         }
22404         return Date.parse(String(s));
22405     },
22406     
22407     /**
22408      * Float sorting
22409      * @param {Mixed} s The value being converted
22410      * @return {Float} The comparison value
22411      */
22412     asFloat : function(s) {
22413         var val = parseFloat(String(s).replace(/,/g, ""));
22414         if(isNaN(val)) {
22415             val = 0;
22416         }
22417         return val;
22418     },
22419     
22420     /**
22421      * Integer sorting
22422      * @param {Mixed} s The value being converted
22423      * @return {Number} The comparison value
22424      */
22425     asInt : function(s) {
22426         var val = parseInt(String(s).replace(/,/g, ""));
22427         if(isNaN(val)) {
22428             val = 0;
22429         }
22430         return val;
22431     }
22432 };/*
22433  * Based on:
22434  * Ext JS Library 1.1.1
22435  * Copyright(c) 2006-2007, Ext JS, LLC.
22436  *
22437  * Originally Released Under LGPL - original licence link has changed is not relivant.
22438  *
22439  * Fork - LGPL
22440  * <script type="text/javascript">
22441  */
22442
22443 /**
22444 * @class Roo.data.Record
22445  * Instances of this class encapsulate both record <em>definition</em> information, and record
22446  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22447  * to access Records cached in an {@link Roo.data.Store} object.<br>
22448  * <p>
22449  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22450  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22451  * objects.<br>
22452  * <p>
22453  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22454  * @constructor
22455  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22456  * {@link #create}. The parameters are the same.
22457  * @param {Array} data An associative Array of data values keyed by the field name.
22458  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22459  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22460  * not specified an integer id is generated.
22461  */
22462 Roo.data.Record = function(data, id){
22463     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22464     this.data = data;
22465 };
22466
22467 /**
22468  * Generate a constructor for a specific record layout.
22469  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22470  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22471  * Each field definition object may contain the following properties: <ul>
22472  * <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,
22473  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22474  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22475  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22476  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22477  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22478  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22479  * this may be omitted.</p></li>
22480  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22481  * <ul><li>auto (Default, implies no conversion)</li>
22482  * <li>string</li>
22483  * <li>int</li>
22484  * <li>float</li>
22485  * <li>boolean</li>
22486  * <li>date</li></ul></p></li>
22487  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22488  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22489  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22490  * by the Reader into an object that will be stored in the Record. It is passed the
22491  * following parameters:<ul>
22492  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22493  * </ul></p></li>
22494  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22495  * </ul>
22496  * <br>usage:<br><pre><code>
22497 var TopicRecord = Roo.data.Record.create(
22498     {name: 'title', mapping: 'topic_title'},
22499     {name: 'author', mapping: 'username'},
22500     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22501     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22502     {name: 'lastPoster', mapping: 'user2'},
22503     {name: 'excerpt', mapping: 'post_text'}
22504 );
22505
22506 var myNewRecord = new TopicRecord({
22507     title: 'Do my job please',
22508     author: 'noobie',
22509     totalPosts: 1,
22510     lastPost: new Date(),
22511     lastPoster: 'Animal',
22512     excerpt: 'No way dude!'
22513 });
22514 myStore.add(myNewRecord);
22515 </code></pre>
22516  * @method create
22517  * @static
22518  */
22519 Roo.data.Record.create = function(o){
22520     var f = function(){
22521         f.superclass.constructor.apply(this, arguments);
22522     };
22523     Roo.extend(f, Roo.data.Record);
22524     var p = f.prototype;
22525     p.fields = new Roo.util.MixedCollection(false, function(field){
22526         return field.name;
22527     });
22528     for(var i = 0, len = o.length; i < len; i++){
22529         p.fields.add(new Roo.data.Field(o[i]));
22530     }
22531     f.getField = function(name){
22532         return p.fields.get(name);  
22533     };
22534     return f;
22535 };
22536
22537 Roo.data.Record.AUTO_ID = 1000;
22538 Roo.data.Record.EDIT = 'edit';
22539 Roo.data.Record.REJECT = 'reject';
22540 Roo.data.Record.COMMIT = 'commit';
22541
22542 Roo.data.Record.prototype = {
22543     /**
22544      * Readonly flag - true if this record has been modified.
22545      * @type Boolean
22546      */
22547     dirty : false,
22548     editing : false,
22549     error: null,
22550     modified: null,
22551
22552     // private
22553     join : function(store){
22554         this.store = store;
22555     },
22556
22557     /**
22558      * Set the named field to the specified value.
22559      * @param {String} name The name of the field to set.
22560      * @param {Object} value The value to set the field to.
22561      */
22562     set : function(name, value){
22563         if(this.data[name] == value){
22564             return;
22565         }
22566         this.dirty = true;
22567         if(!this.modified){
22568             this.modified = {};
22569         }
22570         if(typeof this.modified[name] == 'undefined'){
22571             this.modified[name] = this.data[name];
22572         }
22573         this.data[name] = value;
22574         if(!this.editing && this.store){
22575             this.store.afterEdit(this);
22576         }       
22577     },
22578
22579     /**
22580      * Get the value of the named field.
22581      * @param {String} name The name of the field to get the value of.
22582      * @return {Object} The value of the field.
22583      */
22584     get : function(name){
22585         return this.data[name]; 
22586     },
22587
22588     // private
22589     beginEdit : function(){
22590         this.editing = true;
22591         this.modified = {}; 
22592     },
22593
22594     // private
22595     cancelEdit : function(){
22596         this.editing = false;
22597         delete this.modified;
22598     },
22599
22600     // private
22601     endEdit : function(){
22602         this.editing = false;
22603         if(this.dirty && this.store){
22604             this.store.afterEdit(this);
22605         }
22606     },
22607
22608     /**
22609      * Usually called by the {@link Roo.data.Store} which owns the Record.
22610      * Rejects all changes made to the Record since either creation, or the last commit operation.
22611      * Modified fields are reverted to their original values.
22612      * <p>
22613      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22614      * of reject operations.
22615      */
22616     reject : function(){
22617         var m = this.modified;
22618         for(var n in m){
22619             if(typeof m[n] != "function"){
22620                 this.data[n] = m[n];
22621             }
22622         }
22623         this.dirty = false;
22624         delete this.modified;
22625         this.editing = false;
22626         if(this.store){
22627             this.store.afterReject(this);
22628         }
22629     },
22630
22631     /**
22632      * Usually called by the {@link Roo.data.Store} which owns the Record.
22633      * Commits all changes made to the Record since either creation, or the last commit operation.
22634      * <p>
22635      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22636      * of commit operations.
22637      */
22638     commit : function(){
22639         this.dirty = false;
22640         delete this.modified;
22641         this.editing = false;
22642         if(this.store){
22643             this.store.afterCommit(this);
22644         }
22645     },
22646
22647     // private
22648     hasError : function(){
22649         return this.error != null;
22650     },
22651
22652     // private
22653     clearError : function(){
22654         this.error = null;
22655     },
22656
22657     /**
22658      * Creates a copy of this record.
22659      * @param {String} id (optional) A new record id if you don't want to use this record's id
22660      * @return {Record}
22661      */
22662     copy : function(newId) {
22663         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22664     }
22665 };/*
22666  * Based on:
22667  * Ext JS Library 1.1.1
22668  * Copyright(c) 2006-2007, Ext JS, LLC.
22669  *
22670  * Originally Released Under LGPL - original licence link has changed is not relivant.
22671  *
22672  * Fork - LGPL
22673  * <script type="text/javascript">
22674  */
22675
22676
22677
22678 /**
22679  * @class Roo.data.Store
22680  * @extends Roo.util.Observable
22681  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22682  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22683  * <p>
22684  * 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
22685  * has no knowledge of the format of the data returned by the Proxy.<br>
22686  * <p>
22687  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22688  * instances from the data object. These records are cached and made available through accessor functions.
22689  * @constructor
22690  * Creates a new Store.
22691  * @param {Object} config A config object containing the objects needed for the Store to access data,
22692  * and read the data into Records.
22693  */
22694 Roo.data.Store = function(config){
22695     this.data = new Roo.util.MixedCollection(false);
22696     this.data.getKey = function(o){
22697         return o.id;
22698     };
22699     this.baseParams = {};
22700     // private
22701     this.paramNames = {
22702         "start" : "start",
22703         "limit" : "limit",
22704         "sort" : "sort",
22705         "dir" : "dir",
22706         "multisort" : "_multisort"
22707     };
22708
22709     if(config && config.data){
22710         this.inlineData = config.data;
22711         delete config.data;
22712     }
22713
22714     Roo.apply(this, config);
22715     
22716     if(this.reader){ // reader passed
22717         this.reader = Roo.factory(this.reader, Roo.data);
22718         this.reader.xmodule = this.xmodule || false;
22719         if(!this.recordType){
22720             this.recordType = this.reader.recordType;
22721         }
22722         if(this.reader.onMetaChange){
22723             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22724         }
22725     }
22726
22727     if(this.recordType){
22728         this.fields = this.recordType.prototype.fields;
22729     }
22730     this.modified = [];
22731
22732     this.addEvents({
22733         /**
22734          * @event datachanged
22735          * Fires when the data cache has changed, and a widget which is using this Store
22736          * as a Record cache should refresh its view.
22737          * @param {Store} this
22738          */
22739         datachanged : true,
22740         /**
22741          * @event metachange
22742          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22743          * @param {Store} this
22744          * @param {Object} meta The JSON metadata
22745          */
22746         metachange : true,
22747         /**
22748          * @event add
22749          * Fires when Records have been added to the Store
22750          * @param {Store} this
22751          * @param {Roo.data.Record[]} records The array of Records added
22752          * @param {Number} index The index at which the record(s) were added
22753          */
22754         add : true,
22755         /**
22756          * @event remove
22757          * Fires when a Record has been removed from the Store
22758          * @param {Store} this
22759          * @param {Roo.data.Record} record The Record that was removed
22760          * @param {Number} index The index at which the record was removed
22761          */
22762         remove : true,
22763         /**
22764          * @event update
22765          * Fires when a Record has been updated
22766          * @param {Store} this
22767          * @param {Roo.data.Record} record The Record that was updated
22768          * @param {String} operation The update operation being performed.  Value may be one of:
22769          * <pre><code>
22770  Roo.data.Record.EDIT
22771  Roo.data.Record.REJECT
22772  Roo.data.Record.COMMIT
22773          * </code></pre>
22774          */
22775         update : true,
22776         /**
22777          * @event clear
22778          * Fires when the data cache has been cleared.
22779          * @param {Store} this
22780          */
22781         clear : true,
22782         /**
22783          * @event beforeload
22784          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22785          * the load action will be canceled.
22786          * @param {Store} this
22787          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22788          */
22789         beforeload : true,
22790         /**
22791          * @event beforeloadadd
22792          * Fires after a new set of Records has been loaded.
22793          * @param {Store} this
22794          * @param {Roo.data.Record[]} records The Records that were loaded
22795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22796          */
22797         beforeloadadd : true,
22798         /**
22799          * @event load
22800          * Fires after a new set of Records has been loaded, before they are added to the store.
22801          * @param {Store} this
22802          * @param {Roo.data.Record[]} records The Records that were loaded
22803          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22804          * @params {Object} return from reader
22805          */
22806         load : true,
22807         /**
22808          * @event loadexception
22809          * Fires if an exception occurs in the Proxy during loading.
22810          * Called with the signature of the Proxy's "loadexception" event.
22811          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22812          * 
22813          * @param {Proxy} 
22814          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22815          * @param {Object} load options 
22816          * @param {Object} jsonData from your request (normally this contains the Exception)
22817          */
22818         loadexception : true
22819     });
22820     
22821     if(this.proxy){
22822         this.proxy = Roo.factory(this.proxy, Roo.data);
22823         this.proxy.xmodule = this.xmodule || false;
22824         this.relayEvents(this.proxy,  ["loadexception"]);
22825     }
22826     this.sortToggle = {};
22827     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22828
22829     Roo.data.Store.superclass.constructor.call(this);
22830
22831     if(this.inlineData){
22832         this.loadData(this.inlineData);
22833         delete this.inlineData;
22834     }
22835 };
22836
22837 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22838      /**
22839     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22840     * without a remote query - used by combo/forms at present.
22841     */
22842     
22843     /**
22844     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22845     */
22846     /**
22847     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22848     */
22849     /**
22850     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22851     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22852     */
22853     /**
22854     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22855     * on any HTTP request
22856     */
22857     /**
22858     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22859     */
22860     /**
22861     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22862     */
22863     multiSort: false,
22864     /**
22865     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22866     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22867     */
22868     remoteSort : false,
22869
22870     /**
22871     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22872      * loaded or when a record is removed. (defaults to false).
22873     */
22874     pruneModifiedRecords : false,
22875
22876     // private
22877     lastOptions : null,
22878
22879     /**
22880      * Add Records to the Store and fires the add event.
22881      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22882      */
22883     add : function(records){
22884         records = [].concat(records);
22885         for(var i = 0, len = records.length; i < len; i++){
22886             records[i].join(this);
22887         }
22888         var index = this.data.length;
22889         this.data.addAll(records);
22890         this.fireEvent("add", this, records, index);
22891     },
22892
22893     /**
22894      * Remove a Record from the Store and fires the remove event.
22895      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22896      */
22897     remove : function(record){
22898         var index = this.data.indexOf(record);
22899         this.data.removeAt(index);
22900         if(this.pruneModifiedRecords){
22901             this.modified.remove(record);
22902         }
22903         this.fireEvent("remove", this, record, index);
22904     },
22905
22906     /**
22907      * Remove all Records from the Store and fires the clear event.
22908      */
22909     removeAll : function(){
22910         this.data.clear();
22911         if(this.pruneModifiedRecords){
22912             this.modified = [];
22913         }
22914         this.fireEvent("clear", this);
22915     },
22916
22917     /**
22918      * Inserts Records to the Store at the given index and fires the add event.
22919      * @param {Number} index The start index at which to insert the passed Records.
22920      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22921      */
22922     insert : function(index, records){
22923         records = [].concat(records);
22924         for(var i = 0, len = records.length; i < len; i++){
22925             this.data.insert(index, records[i]);
22926             records[i].join(this);
22927         }
22928         this.fireEvent("add", this, records, index);
22929     },
22930
22931     /**
22932      * Get the index within the cache of the passed Record.
22933      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22934      * @return {Number} The index of the passed Record. Returns -1 if not found.
22935      */
22936     indexOf : function(record){
22937         return this.data.indexOf(record);
22938     },
22939
22940     /**
22941      * Get the index within the cache of the Record with the passed id.
22942      * @param {String} id The id of the Record to find.
22943      * @return {Number} The index of the Record. Returns -1 if not found.
22944      */
22945     indexOfId : function(id){
22946         return this.data.indexOfKey(id);
22947     },
22948
22949     /**
22950      * Get the Record with the specified id.
22951      * @param {String} id The id of the Record to find.
22952      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22953      */
22954     getById : function(id){
22955         return this.data.key(id);
22956     },
22957
22958     /**
22959      * Get the Record at the specified index.
22960      * @param {Number} index The index of the Record to find.
22961      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22962      */
22963     getAt : function(index){
22964         return this.data.itemAt(index);
22965     },
22966
22967     /**
22968      * Returns a range of Records between specified indices.
22969      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22970      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22971      * @return {Roo.data.Record[]} An array of Records
22972      */
22973     getRange : function(start, end){
22974         return this.data.getRange(start, end);
22975     },
22976
22977     // private
22978     storeOptions : function(o){
22979         o = Roo.apply({}, o);
22980         delete o.callback;
22981         delete o.scope;
22982         this.lastOptions = o;
22983     },
22984
22985     /**
22986      * Loads the Record cache from the configured Proxy using the configured Reader.
22987      * <p>
22988      * If using remote paging, then the first load call must specify the <em>start</em>
22989      * and <em>limit</em> properties in the options.params property to establish the initial
22990      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22991      * <p>
22992      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22993      * and this call will return before the new data has been loaded. Perform any post-processing
22994      * in a callback function, or in a "load" event handler.</strong>
22995      * <p>
22996      * @param {Object} options An object containing properties which control loading options:<ul>
22997      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22998      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22999      * passed the following arguments:<ul>
23000      * <li>r : Roo.data.Record[]</li>
23001      * <li>options: Options object from the load call</li>
23002      * <li>success: Boolean success indicator</li></ul></li>
23003      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23004      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23005      * </ul>
23006      */
23007     load : function(options){
23008         options = options || {};
23009         if(this.fireEvent("beforeload", this, options) !== false){
23010             this.storeOptions(options);
23011             var p = Roo.apply(options.params || {}, this.baseParams);
23012             // if meta was not loaded from remote source.. try requesting it.
23013             if (!this.reader.metaFromRemote) {
23014                 p._requestMeta = 1;
23015             }
23016             if(this.sortInfo && this.remoteSort){
23017                 var pn = this.paramNames;
23018                 p[pn["sort"]] = this.sortInfo.field;
23019                 p[pn["dir"]] = this.sortInfo.direction;
23020             }
23021             if (this.multiSort) {
23022                 var pn = this.paramNames;
23023                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23024             }
23025             
23026             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23027         }
23028     },
23029
23030     /**
23031      * Reloads the Record cache from the configured Proxy using the configured Reader and
23032      * the options from the last load operation performed.
23033      * @param {Object} options (optional) An object containing properties which may override the options
23034      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23035      * the most recently used options are reused).
23036      */
23037     reload : function(options){
23038         this.load(Roo.applyIf(options||{}, this.lastOptions));
23039     },
23040
23041     // private
23042     // Called as a callback by the Reader during a load operation.
23043     loadRecords : function(o, options, success){
23044         if(!o || success === false){
23045             if(success !== false){
23046                 this.fireEvent("load", this, [], options, o);
23047             }
23048             if(options.callback){
23049                 options.callback.call(options.scope || this, [], options, false);
23050             }
23051             return;
23052         }
23053         // if data returned failure - throw an exception.
23054         if (o.success === false) {
23055             // show a message if no listener is registered.
23056             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23057                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23058             }
23059             // loadmask wil be hooked into this..
23060             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23061             return;
23062         }
23063         var r = o.records, t = o.totalRecords || r.length;
23064         
23065         this.fireEvent("beforeloadadd", this, r, options, o);
23066         
23067         if(!options || options.add !== true){
23068             if(this.pruneModifiedRecords){
23069                 this.modified = [];
23070             }
23071             for(var i = 0, len = r.length; i < len; i++){
23072                 r[i].join(this);
23073             }
23074             if(this.snapshot){
23075                 this.data = this.snapshot;
23076                 delete this.snapshot;
23077             }
23078             this.data.clear();
23079             this.data.addAll(r);
23080             this.totalLength = t;
23081             this.applySort();
23082             this.fireEvent("datachanged", this);
23083         }else{
23084             this.totalLength = Math.max(t, this.data.length+r.length);
23085             this.add(r);
23086         }
23087         
23088         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23089                 
23090             var e = new Roo.data.Record({});
23091
23092             e.set(this.parent.displayField, this.parent.emptyTitle);
23093             e.set(this.parent.valueField, '');
23094
23095             this.insert(0, e);
23096         }
23097             
23098         this.fireEvent("load", this, r, options, o);
23099         if(options.callback){
23100             options.callback.call(options.scope || this, r, options, true);
23101         }
23102     },
23103
23104
23105     /**
23106      * Loads data from a passed data block. A Reader which understands the format of the data
23107      * must have been configured in the constructor.
23108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23111      */
23112     loadData : function(o, append){
23113         var r = this.reader.readRecords(o);
23114         this.loadRecords(r, {add: append}, true);
23115     },
23116
23117     /**
23118      * Gets the number of cached records.
23119      * <p>
23120      * <em>If using paging, this may not be the total size of the dataset. If the data object
23121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23122      * the data set size</em>
23123      */
23124     getCount : function(){
23125         return this.data.length || 0;
23126     },
23127
23128     /**
23129      * Gets the total number of records in the dataset as returned by the server.
23130      * <p>
23131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23132      * the dataset size</em>
23133      */
23134     getTotalCount : function(){
23135         return this.totalLength || 0;
23136     },
23137
23138     /**
23139      * Returns the sort state of the Store as an object with two properties:
23140      * <pre><code>
23141  field {String} The name of the field by which the Records are sorted
23142  direction {String} The sort order, "ASC" or "DESC"
23143      * </code></pre>
23144      */
23145     getSortState : function(){
23146         return this.sortInfo;
23147     },
23148
23149     // private
23150     applySort : function(){
23151         if(this.sortInfo && !this.remoteSort){
23152             var s = this.sortInfo, f = s.field;
23153             var st = this.fields.get(f).sortType;
23154             var fn = function(r1, r2){
23155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23157             };
23158             this.data.sort(s.direction, fn);
23159             if(this.snapshot && this.snapshot != this.data){
23160                 this.snapshot.sort(s.direction, fn);
23161             }
23162         }
23163     },
23164
23165     /**
23166      * Sets the default sort column and order to be used by the next load operation.
23167      * @param {String} fieldName The name of the field to sort by.
23168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23169      */
23170     setDefaultSort : function(field, dir){
23171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23172     },
23173
23174     /**
23175      * Sort the Records.
23176      * If remote sorting is used, the sort is performed on the server, and the cache is
23177      * reloaded. If local sorting is used, the cache is sorted internally.
23178      * @param {String} fieldName The name of the field to sort by.
23179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23180      */
23181     sort : function(fieldName, dir){
23182         var f = this.fields.get(fieldName);
23183         if(!dir){
23184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23185             
23186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23188             }else{
23189                 dir = f.sortDir;
23190             }
23191         }
23192         this.sortToggle[f.name] = dir;
23193         this.sortInfo = {field: f.name, direction: dir};
23194         if(!this.remoteSort){
23195             this.applySort();
23196             this.fireEvent("datachanged", this);
23197         }else{
23198             this.load(this.lastOptions);
23199         }
23200     },
23201
23202     /**
23203      * Calls the specified function for each of the Records in the cache.
23204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23205      * Returning <em>false</em> aborts and exits the iteration.
23206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23207      */
23208     each : function(fn, scope){
23209         this.data.each(fn, scope);
23210     },
23211
23212     /**
23213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23214      * (e.g., during paging).
23215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23216      */
23217     getModifiedRecords : function(){
23218         return this.modified;
23219     },
23220
23221     // private
23222     createFilterFn : function(property, value, anyMatch){
23223         if(!value.exec){ // not a regex
23224             value = String(value);
23225             if(value.length == 0){
23226                 return false;
23227             }
23228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23229         }
23230         return function(r){
23231             return value.test(r.data[property]);
23232         };
23233     },
23234
23235     /**
23236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23237      * @param {String} property A field on your records
23238      * @param {Number} start The record index to start at (defaults to 0)
23239      * @param {Number} end The last record index to include (defaults to length - 1)
23240      * @return {Number} The sum
23241      */
23242     sum : function(property, start, end){
23243         var rs = this.data.items, v = 0;
23244         start = start || 0;
23245         end = (end || end === 0) ? end : rs.length-1;
23246
23247         for(var i = start; i <= end; i++){
23248             v += (rs[i].data[property] || 0);
23249         }
23250         return v;
23251     },
23252
23253     /**
23254      * Filter the records by a specified property.
23255      * @param {String} field A field on your records
23256      * @param {String/RegExp} value Either a string that the field
23257      * should start with or a RegExp to test against the field
23258      * @param {Boolean} anyMatch True to match any part not just the beginning
23259      */
23260     filter : function(property, value, anyMatch){
23261         var fn = this.createFilterFn(property, value, anyMatch);
23262         return fn ? this.filterBy(fn) : this.clearFilter();
23263     },
23264
23265     /**
23266      * Filter by a function. The specified function will be called with each
23267      * record in this data source. If the function returns true the record is included,
23268      * otherwise it is filtered.
23269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23270      * @param {Object} scope (optional) The scope of the function (defaults to this)
23271      */
23272     filterBy : function(fn, scope){
23273         this.snapshot = this.snapshot || this.data;
23274         this.data = this.queryBy(fn, scope||this);
23275         this.fireEvent("datachanged", this);
23276     },
23277
23278     /**
23279      * Query the records by a specified property.
23280      * @param {String} field A field on your records
23281      * @param {String/RegExp} value Either a string that the field
23282      * should start with or a RegExp to test against the field
23283      * @param {Boolean} anyMatch True to match any part not just the beginning
23284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23285      */
23286     query : function(property, value, anyMatch){
23287         var fn = this.createFilterFn(property, value, anyMatch);
23288         return fn ? this.queryBy(fn) : this.data.clone();
23289     },
23290
23291     /**
23292      * Query by a function. The specified function will be called with each
23293      * record in this data source. If the function returns true the record is included
23294      * in the results.
23295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23296      * @param {Object} scope (optional) The scope of the function (defaults to this)
23297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23298      **/
23299     queryBy : function(fn, scope){
23300         var data = this.snapshot || this.data;
23301         return data.filterBy(fn, scope||this);
23302     },
23303
23304     /**
23305      * Collects unique values for a particular dataIndex from this store.
23306      * @param {String} dataIndex The property to collect
23307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23309      * @return {Array} An array of the unique values
23310      **/
23311     collect : function(dataIndex, allowNull, bypassFilter){
23312         var d = (bypassFilter === true && this.snapshot) ?
23313                 this.snapshot.items : this.data.items;
23314         var v, sv, r = [], l = {};
23315         for(var i = 0, len = d.length; i < len; i++){
23316             v = d[i].data[dataIndex];
23317             sv = String(v);
23318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23319                 l[sv] = true;
23320                 r[r.length] = v;
23321             }
23322         }
23323         return r;
23324     },
23325
23326     /**
23327      * Revert to a view of the Record cache with no filtering applied.
23328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23329      */
23330     clearFilter : function(suppressEvent){
23331         if(this.snapshot && this.snapshot != this.data){
23332             this.data = this.snapshot;
23333             delete this.snapshot;
23334             if(suppressEvent !== true){
23335                 this.fireEvent("datachanged", this);
23336             }
23337         }
23338     },
23339
23340     // private
23341     afterEdit : function(record){
23342         if(this.modified.indexOf(record) == -1){
23343             this.modified.push(record);
23344         }
23345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23346     },
23347     
23348     // private
23349     afterReject : function(record){
23350         this.modified.remove(record);
23351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23352     },
23353
23354     // private
23355     afterCommit : function(record){
23356         this.modified.remove(record);
23357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23358     },
23359
23360     /**
23361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23363      */
23364     commitChanges : function(){
23365         var m = this.modified.slice(0);
23366         this.modified = [];
23367         for(var i = 0, len = m.length; i < len; i++){
23368             m[i].commit();
23369         }
23370     },
23371
23372     /**
23373      * Cancel outstanding changes on all changed records.
23374      */
23375     rejectChanges : function(){
23376         var m = this.modified.slice(0);
23377         this.modified = [];
23378         for(var i = 0, len = m.length; i < len; i++){
23379             m[i].reject();
23380         }
23381     },
23382
23383     onMetaChange : function(meta, rtype, o){
23384         this.recordType = rtype;
23385         this.fields = rtype.prototype.fields;
23386         delete this.snapshot;
23387         this.sortInfo = meta.sortInfo || this.sortInfo;
23388         this.modified = [];
23389         this.fireEvent('metachange', this, this.reader.meta);
23390     },
23391     
23392     moveIndex : function(data, type)
23393     {
23394         var index = this.indexOf(data);
23395         
23396         var newIndex = index + type;
23397         
23398         this.remove(data);
23399         
23400         this.insert(newIndex, data);
23401         
23402     }
23403 });/*
23404  * Based on:
23405  * Ext JS Library 1.1.1
23406  * Copyright(c) 2006-2007, Ext JS, LLC.
23407  *
23408  * Originally Released Under LGPL - original licence link has changed is not relivant.
23409  *
23410  * Fork - LGPL
23411  * <script type="text/javascript">
23412  */
23413
23414 /**
23415  * @class Roo.data.SimpleStore
23416  * @extends Roo.data.Store
23417  * Small helper class to make creating Stores from Array data easier.
23418  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23419  * @cfg {Array} fields An array of field definition objects, or field name strings.
23420  * @cfg {Array} data The multi-dimensional array of data
23421  * @constructor
23422  * @param {Object} config
23423  */
23424 Roo.data.SimpleStore = function(config){
23425     Roo.data.SimpleStore.superclass.constructor.call(this, {
23426         isLocal : true,
23427         reader: new Roo.data.ArrayReader({
23428                 id: config.id
23429             },
23430             Roo.data.Record.create(config.fields)
23431         ),
23432         proxy : new Roo.data.MemoryProxy(config.data)
23433     });
23434     this.load();
23435 };
23436 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23437  * Based on:
23438  * Ext JS Library 1.1.1
23439  * Copyright(c) 2006-2007, Ext JS, LLC.
23440  *
23441  * Originally Released Under LGPL - original licence link has changed is not relivant.
23442  *
23443  * Fork - LGPL
23444  * <script type="text/javascript">
23445  */
23446
23447 /**
23448 /**
23449  * @extends Roo.data.Store
23450  * @class Roo.data.JsonStore
23451  * Small helper class to make creating Stores for JSON data easier. <br/>
23452 <pre><code>
23453 var store = new Roo.data.JsonStore({
23454     url: 'get-images.php',
23455     root: 'images',
23456     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23457 });
23458 </code></pre>
23459  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23460  * JsonReader and HttpProxy (unless inline data is provided).</b>
23461  * @cfg {Array} fields An array of field definition objects, or field name strings.
23462  * @constructor
23463  * @param {Object} config
23464  */
23465 Roo.data.JsonStore = function(c){
23466     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23467         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23468         reader: new Roo.data.JsonReader(c, c.fields)
23469     }));
23470 };
23471 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23472  * Based on:
23473  * Ext JS Library 1.1.1
23474  * Copyright(c) 2006-2007, Ext JS, LLC.
23475  *
23476  * Originally Released Under LGPL - original licence link has changed is not relivant.
23477  *
23478  * Fork - LGPL
23479  * <script type="text/javascript">
23480  */
23481
23482  
23483 Roo.data.Field = function(config){
23484     if(typeof config == "string"){
23485         config = {name: config};
23486     }
23487     Roo.apply(this, config);
23488     
23489     if(!this.type){
23490         this.type = "auto";
23491     }
23492     
23493     var st = Roo.data.SortTypes;
23494     // named sortTypes are supported, here we look them up
23495     if(typeof this.sortType == "string"){
23496         this.sortType = st[this.sortType];
23497     }
23498     
23499     // set default sortType for strings and dates
23500     if(!this.sortType){
23501         switch(this.type){
23502             case "string":
23503                 this.sortType = st.asUCString;
23504                 break;
23505             case "date":
23506                 this.sortType = st.asDate;
23507                 break;
23508             default:
23509                 this.sortType = st.none;
23510         }
23511     }
23512
23513     // define once
23514     var stripRe = /[\$,%]/g;
23515
23516     // prebuilt conversion function for this field, instead of
23517     // switching every time we're reading a value
23518     if(!this.convert){
23519         var cv, dateFormat = this.dateFormat;
23520         switch(this.type){
23521             case "":
23522             case "auto":
23523             case undefined:
23524                 cv = function(v){ return v; };
23525                 break;
23526             case "string":
23527                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23528                 break;
23529             case "int":
23530                 cv = function(v){
23531                     return v !== undefined && v !== null && v !== '' ?
23532                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23533                     };
23534                 break;
23535             case "float":
23536                 cv = function(v){
23537                     return v !== undefined && v !== null && v !== '' ?
23538                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23539                     };
23540                 break;
23541             case "bool":
23542             case "boolean":
23543                 cv = function(v){ return v === true || v === "true" || v == 1; };
23544                 break;
23545             case "date":
23546                 cv = function(v){
23547                     if(!v){
23548                         return '';
23549                     }
23550                     if(v instanceof Date){
23551                         return v;
23552                     }
23553                     if(dateFormat){
23554                         if(dateFormat == "timestamp"){
23555                             return new Date(v*1000);
23556                         }
23557                         return Date.parseDate(v, dateFormat);
23558                     }
23559                     var parsed = Date.parse(v);
23560                     return parsed ? new Date(parsed) : null;
23561                 };
23562              break;
23563             
23564         }
23565         this.convert = cv;
23566     }
23567 };
23568
23569 Roo.data.Field.prototype = {
23570     dateFormat: null,
23571     defaultValue: "",
23572     mapping: null,
23573     sortType : null,
23574     sortDir : "ASC"
23575 };/*
23576  * Based on:
23577  * Ext JS Library 1.1.1
23578  * Copyright(c) 2006-2007, Ext JS, LLC.
23579  *
23580  * Originally Released Under LGPL - original licence link has changed is not relivant.
23581  *
23582  * Fork - LGPL
23583  * <script type="text/javascript">
23584  */
23585  
23586 // Base class for reading structured data from a data source.  This class is intended to be
23587 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23588
23589 /**
23590  * @class Roo.data.DataReader
23591  * Base class for reading structured data from a data source.  This class is intended to be
23592  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23593  */
23594
23595 Roo.data.DataReader = function(meta, recordType){
23596     
23597     this.meta = meta;
23598     
23599     this.recordType = recordType instanceof Array ? 
23600         Roo.data.Record.create(recordType) : recordType;
23601 };
23602
23603 Roo.data.DataReader.prototype = {
23604      /**
23605      * Create an empty record
23606      * @param {Object} data (optional) - overlay some values
23607      * @return {Roo.data.Record} record created.
23608      */
23609     newRow :  function(d) {
23610         var da =  {};
23611         this.recordType.prototype.fields.each(function(c) {
23612             switch( c.type) {
23613                 case 'int' : da[c.name] = 0; break;
23614                 case 'date' : da[c.name] = new Date(); break;
23615                 case 'float' : da[c.name] = 0.0; break;
23616                 case 'boolean' : da[c.name] = false; break;
23617                 default : da[c.name] = ""; break;
23618             }
23619             
23620         });
23621         return new this.recordType(Roo.apply(da, d));
23622     }
23623     
23624 };/*
23625  * Based on:
23626  * Ext JS Library 1.1.1
23627  * Copyright(c) 2006-2007, Ext JS, LLC.
23628  *
23629  * Originally Released Under LGPL - original licence link has changed is not relivant.
23630  *
23631  * Fork - LGPL
23632  * <script type="text/javascript">
23633  */
23634
23635 /**
23636  * @class Roo.data.DataProxy
23637  * @extends Roo.data.Observable
23638  * This class is an abstract base class for implementations which provide retrieval of
23639  * unformatted data objects.<br>
23640  * <p>
23641  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23642  * (of the appropriate type which knows how to parse the data object) to provide a block of
23643  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23644  * <p>
23645  * Custom implementations must implement the load method as described in
23646  * {@link Roo.data.HttpProxy#load}.
23647  */
23648 Roo.data.DataProxy = function(){
23649     this.addEvents({
23650         /**
23651          * @event beforeload
23652          * Fires before a network request is made to retrieve a data object.
23653          * @param {Object} This DataProxy object.
23654          * @param {Object} params The params parameter to the load function.
23655          */
23656         beforeload : true,
23657         /**
23658          * @event load
23659          * Fires before the load method's callback is called.
23660          * @param {Object} This DataProxy object.
23661          * @param {Object} o The data object.
23662          * @param {Object} arg The callback argument object passed to the load function.
23663          */
23664         load : true,
23665         /**
23666          * @event loadexception
23667          * Fires if an Exception occurs during data retrieval.
23668          * @param {Object} This DataProxy object.
23669          * @param {Object} o The data object.
23670          * @param {Object} arg The callback argument object passed to the load function.
23671          * @param {Object} e The Exception.
23672          */
23673         loadexception : true
23674     });
23675     Roo.data.DataProxy.superclass.constructor.call(this);
23676 };
23677
23678 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23679
23680     /**
23681      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23682      */
23683 /*
23684  * Based on:
23685  * Ext JS Library 1.1.1
23686  * Copyright(c) 2006-2007, Ext JS, LLC.
23687  *
23688  * Originally Released Under LGPL - original licence link has changed is not relivant.
23689  *
23690  * Fork - LGPL
23691  * <script type="text/javascript">
23692  */
23693 /**
23694  * @class Roo.data.MemoryProxy
23695  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23696  * to the Reader when its load method is called.
23697  * @constructor
23698  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23699  */
23700 Roo.data.MemoryProxy = function(data){
23701     if (data.data) {
23702         data = data.data;
23703     }
23704     Roo.data.MemoryProxy.superclass.constructor.call(this);
23705     this.data = data;
23706 };
23707
23708 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23709     
23710     /**
23711      * Load data from the requested source (in this case an in-memory
23712      * data object passed to the constructor), read the data object into
23713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23714      * process that block using the passed callback.
23715      * @param {Object} params This parameter is not used by the MemoryProxy class.
23716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23717      * object into a block of Roo.data.Records.
23718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23719      * The function must be passed <ul>
23720      * <li>The Record block object</li>
23721      * <li>The "arg" argument from the load function</li>
23722      * <li>A boolean success indicator</li>
23723      * </ul>
23724      * @param {Object} scope The scope in which to call the callback
23725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23726      */
23727     load : function(params, reader, callback, scope, arg){
23728         params = params || {};
23729         var result;
23730         try {
23731             result = reader.readRecords(this.data);
23732         }catch(e){
23733             this.fireEvent("loadexception", this, arg, null, e);
23734             callback.call(scope, null, arg, false);
23735             return;
23736         }
23737         callback.call(scope, result, arg, true);
23738     },
23739     
23740     // private
23741     update : function(params, records){
23742         
23743     }
23744 });/*
23745  * Based on:
23746  * Ext JS Library 1.1.1
23747  * Copyright(c) 2006-2007, Ext JS, LLC.
23748  *
23749  * Originally Released Under LGPL - original licence link has changed is not relivant.
23750  *
23751  * Fork - LGPL
23752  * <script type="text/javascript">
23753  */
23754 /**
23755  * @class Roo.data.HttpProxy
23756  * @extends Roo.data.DataProxy
23757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23758  * configured to reference a certain URL.<br><br>
23759  * <p>
23760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23761  * from which the running page was served.<br><br>
23762  * <p>
23763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23764  * <p>
23765  * Be aware that to enable the browser to parse an XML document, the server must set
23766  * the Content-Type header in the HTTP response to "text/xml".
23767  * @constructor
23768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23770  * will be used to make the request.
23771  */
23772 Roo.data.HttpProxy = function(conn){
23773     Roo.data.HttpProxy.superclass.constructor.call(this);
23774     // is conn a conn config or a real conn?
23775     this.conn = conn;
23776     this.useAjax = !conn || !conn.events;
23777   
23778 };
23779
23780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23781     // thse are take from connection...
23782     
23783     /**
23784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23785      */
23786     /**
23787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23788      * extra parameters to each request made by this object. (defaults to undefined)
23789      */
23790     /**
23791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23792      *  to each request made by this object. (defaults to undefined)
23793      */
23794     /**
23795      * @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)
23796      */
23797     /**
23798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23799      */
23800      /**
23801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23802      * @type Boolean
23803      */
23804   
23805
23806     /**
23807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23808      * @type Boolean
23809      */
23810     /**
23811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23813      * a finer-grained basis than the DataProxy events.
23814      */
23815     getConnection : function(){
23816         return this.useAjax ? Roo.Ajax : this.conn;
23817     },
23818
23819     /**
23820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23822      * process that block using the passed callback.
23823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23824      * for the request to the remote server.
23825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23826      * object into a block of Roo.data.Records.
23827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23828      * The function must be passed <ul>
23829      * <li>The Record block object</li>
23830      * <li>The "arg" argument from the load function</li>
23831      * <li>A boolean success indicator</li>
23832      * </ul>
23833      * @param {Object} scope The scope in which to call the callback
23834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23835      */
23836     load : function(params, reader, callback, scope, arg){
23837         if(this.fireEvent("beforeload", this, params) !== false){
23838             var  o = {
23839                 params : params || {},
23840                 request: {
23841                     callback : callback,
23842                     scope : scope,
23843                     arg : arg
23844                 },
23845                 reader: reader,
23846                 callback : this.loadResponse,
23847                 scope: this
23848             };
23849             if(this.useAjax){
23850                 Roo.applyIf(o, this.conn);
23851                 if(this.activeRequest){
23852                     Roo.Ajax.abort(this.activeRequest);
23853                 }
23854                 this.activeRequest = Roo.Ajax.request(o);
23855             }else{
23856                 this.conn.request(o);
23857             }
23858         }else{
23859             callback.call(scope||this, null, arg, false);
23860         }
23861     },
23862
23863     // private
23864     loadResponse : function(o, success, response){
23865         delete this.activeRequest;
23866         if(!success){
23867             this.fireEvent("loadexception", this, o, response);
23868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23869             return;
23870         }
23871         var result;
23872         try {
23873             result = o.reader.read(response);
23874         }catch(e){
23875             this.fireEvent("loadexception", this, o, response, e);
23876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23877             return;
23878         }
23879         
23880         this.fireEvent("load", this, o, o.request.arg);
23881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23882     },
23883
23884     // private
23885     update : function(dataSet){
23886
23887     },
23888
23889     // private
23890     updateResponse : function(dataSet){
23891
23892     }
23893 });/*
23894  * Based on:
23895  * Ext JS Library 1.1.1
23896  * Copyright(c) 2006-2007, Ext JS, LLC.
23897  *
23898  * Originally Released Under LGPL - original licence link has changed is not relivant.
23899  *
23900  * Fork - LGPL
23901  * <script type="text/javascript">
23902  */
23903
23904 /**
23905  * @class Roo.data.ScriptTagProxy
23906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23907  * other than the originating domain of the running page.<br><br>
23908  * <p>
23909  * <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
23910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23911  * <p>
23912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23913  * source code that is used as the source inside a &lt;script> tag.<br><br>
23914  * <p>
23915  * In order for the browser to process the returned data, the server must wrap the data object
23916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23918  * depending on whether the callback name was passed:
23919  * <p>
23920  * <pre><code>
23921 boolean scriptTag = false;
23922 String cb = request.getParameter("callback");
23923 if (cb != null) {
23924     scriptTag = true;
23925     response.setContentType("text/javascript");
23926 } else {
23927     response.setContentType("application/x-json");
23928 }
23929 Writer out = response.getWriter();
23930 if (scriptTag) {
23931     out.write(cb + "(");
23932 }
23933 out.print(dataBlock.toJsonString());
23934 if (scriptTag) {
23935     out.write(");");
23936 }
23937 </pre></code>
23938  *
23939  * @constructor
23940  * @param {Object} config A configuration object.
23941  */
23942 Roo.data.ScriptTagProxy = function(config){
23943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23944     Roo.apply(this, config);
23945     this.head = document.getElementsByTagName("head")[0];
23946 };
23947
23948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23949
23950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23951     /**
23952      * @cfg {String} url The URL from which to request the data object.
23953      */
23954     /**
23955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23956      */
23957     timeout : 30000,
23958     /**
23959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23960      * the server the name of the callback function set up by the load call to process the returned data object.
23961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23962      * javascript output which calls this named function passing the data object as its only parameter.
23963      */
23964     callbackParam : "callback",
23965     /**
23966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23967      * name to the request.
23968      */
23969     nocache : true,
23970
23971     /**
23972      * Load data from the configured URL, read the data object into
23973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23974      * process that block using the passed callback.
23975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23976      * for the request to the remote server.
23977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23978      * object into a block of Roo.data.Records.
23979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23980      * The function must be passed <ul>
23981      * <li>The Record block object</li>
23982      * <li>The "arg" argument from the load function</li>
23983      * <li>A boolean success indicator</li>
23984      * </ul>
23985      * @param {Object} scope The scope in which to call the callback
23986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23987      */
23988     load : function(params, reader, callback, scope, arg){
23989         if(this.fireEvent("beforeload", this, params) !== false){
23990
23991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23992
23993             var url = this.url;
23994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23995             if(this.nocache){
23996                 url += "&_dc=" + (new Date().getTime());
23997             }
23998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23999             var trans = {
24000                 id : transId,
24001                 cb : "stcCallback"+transId,
24002                 scriptId : "stcScript"+transId,
24003                 params : params,
24004                 arg : arg,
24005                 url : url,
24006                 callback : callback,
24007                 scope : scope,
24008                 reader : reader
24009             };
24010             var conn = this;
24011
24012             window[trans.cb] = function(o){
24013                 conn.handleResponse(o, trans);
24014             };
24015
24016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24017
24018             if(this.autoAbort !== false){
24019                 this.abort();
24020             }
24021
24022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24023
24024             var script = document.createElement("script");
24025             script.setAttribute("src", url);
24026             script.setAttribute("type", "text/javascript");
24027             script.setAttribute("id", trans.scriptId);
24028             this.head.appendChild(script);
24029
24030             this.trans = trans;
24031         }else{
24032             callback.call(scope||this, null, arg, false);
24033         }
24034     },
24035
24036     // private
24037     isLoading : function(){
24038         return this.trans ? true : false;
24039     },
24040
24041     /**
24042      * Abort the current server request.
24043      */
24044     abort : function(){
24045         if(this.isLoading()){
24046             this.destroyTrans(this.trans);
24047         }
24048     },
24049
24050     // private
24051     destroyTrans : function(trans, isLoaded){
24052         this.head.removeChild(document.getElementById(trans.scriptId));
24053         clearTimeout(trans.timeoutId);
24054         if(isLoaded){
24055             window[trans.cb] = undefined;
24056             try{
24057                 delete window[trans.cb];
24058             }catch(e){}
24059         }else{
24060             // if hasn't been loaded, wait for load to remove it to prevent script error
24061             window[trans.cb] = function(){
24062                 window[trans.cb] = undefined;
24063                 try{
24064                     delete window[trans.cb];
24065                 }catch(e){}
24066             };
24067         }
24068     },
24069
24070     // private
24071     handleResponse : function(o, trans){
24072         this.trans = false;
24073         this.destroyTrans(trans, true);
24074         var result;
24075         try {
24076             result = trans.reader.readRecords(o);
24077         }catch(e){
24078             this.fireEvent("loadexception", this, o, trans.arg, e);
24079             trans.callback.call(trans.scope||window, null, trans.arg, false);
24080             return;
24081         }
24082         this.fireEvent("load", this, o, trans.arg);
24083         trans.callback.call(trans.scope||window, result, trans.arg, true);
24084     },
24085
24086     // private
24087     handleFailure : function(trans){
24088         this.trans = false;
24089         this.destroyTrans(trans, false);
24090         this.fireEvent("loadexception", this, null, trans.arg);
24091         trans.callback.call(trans.scope||window, null, trans.arg, false);
24092     }
24093 });/*
24094  * Based on:
24095  * Ext JS Library 1.1.1
24096  * Copyright(c) 2006-2007, Ext JS, LLC.
24097  *
24098  * Originally Released Under LGPL - original licence link has changed is not relivant.
24099  *
24100  * Fork - LGPL
24101  * <script type="text/javascript">
24102  */
24103
24104 /**
24105  * @class Roo.data.JsonReader
24106  * @extends Roo.data.DataReader
24107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24108  * based on mappings in a provided Roo.data.Record constructor.
24109  * 
24110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24111  * in the reply previously. 
24112  * 
24113  * <p>
24114  * Example code:
24115  * <pre><code>
24116 var RecordDef = Roo.data.Record.create([
24117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24119 ]);
24120 var myReader = new Roo.data.JsonReader({
24121     totalProperty: "results",    // The property which contains the total dataset size (optional)
24122     root: "rows",                // The property which contains an Array of row objects
24123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24124 }, RecordDef);
24125 </code></pre>
24126  * <p>
24127  * This would consume a JSON file like this:
24128  * <pre><code>
24129 { 'results': 2, 'rows': [
24130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24132 }
24133 </code></pre>
24134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24136  * paged from the remote server.
24137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24138  * @cfg {String} root name of the property which contains the Array of row objects.
24139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24140  * @cfg {Array} fields Array of field definition objects
24141  * @constructor
24142  * Create a new JsonReader
24143  * @param {Object} meta Metadata configuration options
24144  * @param {Object} recordType Either an Array of field definition objects,
24145  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24146  */
24147 Roo.data.JsonReader = function(meta, recordType){
24148     
24149     meta = meta || {};
24150     // set some defaults:
24151     Roo.applyIf(meta, {
24152         totalProperty: 'total',
24153         successProperty : 'success',
24154         root : 'data',
24155         id : 'id'
24156     });
24157     
24158     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24159 };
24160 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24161     
24162     /**
24163      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24164      * Used by Store query builder to append _requestMeta to params.
24165      * 
24166      */
24167     metaFromRemote : false,
24168     /**
24169      * This method is only used by a DataProxy which has retrieved data from a remote server.
24170      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24171      * @return {Object} data A data block which is used by an Roo.data.Store object as
24172      * a cache of Roo.data.Records.
24173      */
24174     read : function(response){
24175         var json = response.responseText;
24176        
24177         var o = /* eval:var:o */ eval("("+json+")");
24178         if(!o) {
24179             throw {message: "JsonReader.read: Json object not found"};
24180         }
24181         
24182         if(o.metaData){
24183             
24184             delete this.ef;
24185             this.metaFromRemote = true;
24186             this.meta = o.metaData;
24187             this.recordType = Roo.data.Record.create(o.metaData.fields);
24188             this.onMetaChange(this.meta, this.recordType, o);
24189         }
24190         return this.readRecords(o);
24191     },
24192
24193     // private function a store will implement
24194     onMetaChange : function(meta, recordType, o){
24195
24196     },
24197
24198     /**
24199          * @ignore
24200          */
24201     simpleAccess: function(obj, subsc) {
24202         return obj[subsc];
24203     },
24204
24205         /**
24206          * @ignore
24207          */
24208     getJsonAccessor: function(){
24209         var re = /[\[\.]/;
24210         return function(expr) {
24211             try {
24212                 return(re.test(expr))
24213                     ? new Function("obj", "return obj." + expr)
24214                     : function(obj){
24215                         return obj[expr];
24216                     };
24217             } catch(e){}
24218             return Roo.emptyFn;
24219         };
24220     }(),
24221
24222     /**
24223      * Create a data block containing Roo.data.Records from an XML document.
24224      * @param {Object} o An object which contains an Array of row objects in the property specified
24225      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24226      * which contains the total size of the dataset.
24227      * @return {Object} data A data block which is used by an Roo.data.Store object as
24228      * a cache of Roo.data.Records.
24229      */
24230     readRecords : function(o){
24231         /**
24232          * After any data loads, the raw JSON data is available for further custom processing.
24233          * @type Object
24234          */
24235         this.o = o;
24236         var s = this.meta, Record = this.recordType,
24237             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24238
24239 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24240         if (!this.ef) {
24241             if(s.totalProperty) {
24242                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24243                 }
24244                 if(s.successProperty) {
24245                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24246                 }
24247                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24248                 if (s.id) {
24249                         var g = this.getJsonAccessor(s.id);
24250                         this.getId = function(rec) {
24251                                 var r = g(rec);  
24252                                 return (r === undefined || r === "") ? null : r;
24253                         };
24254                 } else {
24255                         this.getId = function(){return null;};
24256                 }
24257             this.ef = [];
24258             for(var jj = 0; jj < fl; jj++){
24259                 f = fi[jj];
24260                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24261                 this.ef[jj] = this.getJsonAccessor(map);
24262             }
24263         }
24264
24265         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24266         if(s.totalProperty){
24267             var vt = parseInt(this.getTotal(o), 10);
24268             if(!isNaN(vt)){
24269                 totalRecords = vt;
24270             }
24271         }
24272         if(s.successProperty){
24273             var vs = this.getSuccess(o);
24274             if(vs === false || vs === 'false'){
24275                 success = false;
24276             }
24277         }
24278         var records = [];
24279         for(var i = 0; i < c; i++){
24280                 var n = root[i];
24281             var values = {};
24282             var id = this.getId(n);
24283             for(var j = 0; j < fl; j++){
24284                 f = fi[j];
24285             var v = this.ef[j](n);
24286             if (!f.convert) {
24287                 Roo.log('missing convert for ' + f.name);
24288                 Roo.log(f);
24289                 continue;
24290             }
24291             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24292             }
24293             var record = new Record(values, id);
24294             record.json = n;
24295             records[i] = record;
24296         }
24297         return {
24298             raw : o,
24299             success : success,
24300             records : records,
24301             totalRecords : totalRecords
24302         };
24303     }
24304 });/*
24305  * Based on:
24306  * Ext JS Library 1.1.1
24307  * Copyright(c) 2006-2007, Ext JS, LLC.
24308  *
24309  * Originally Released Under LGPL - original licence link has changed is not relivant.
24310  *
24311  * Fork - LGPL
24312  * <script type="text/javascript">
24313  */
24314
24315 /**
24316  * @class Roo.data.XmlReader
24317  * @extends Roo.data.DataReader
24318  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24319  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24320  * <p>
24321  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24322  * header in the HTTP response must be set to "text/xml".</em>
24323  * <p>
24324  * Example code:
24325  * <pre><code>
24326 var RecordDef = Roo.data.Record.create([
24327    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24328    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24329 ]);
24330 var myReader = new Roo.data.XmlReader({
24331    totalRecords: "results", // The element which contains the total dataset size (optional)
24332    record: "row",           // The repeated element which contains row information
24333    id: "id"                 // The element within the row that provides an ID for the record (optional)
24334 }, RecordDef);
24335 </code></pre>
24336  * <p>
24337  * This would consume an XML file like this:
24338  * <pre><code>
24339 &lt;?xml?>
24340 &lt;dataset>
24341  &lt;results>2&lt;/results>
24342  &lt;row>
24343    &lt;id>1&lt;/id>
24344    &lt;name>Bill&lt;/name>
24345    &lt;occupation>Gardener&lt;/occupation>
24346  &lt;/row>
24347  &lt;row>
24348    &lt;id>2&lt;/id>
24349    &lt;name>Ben&lt;/name>
24350    &lt;occupation>Horticulturalist&lt;/occupation>
24351  &lt;/row>
24352 &lt;/dataset>
24353 </code></pre>
24354  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24355  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24356  * paged from the remote server.
24357  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24358  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24359  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24360  * a record identifier value.
24361  * @constructor
24362  * Create a new XmlReader
24363  * @param {Object} meta Metadata configuration options
24364  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24365  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24366  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24367  */
24368 Roo.data.XmlReader = function(meta, recordType){
24369     meta = meta || {};
24370     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24371 };
24372 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24373     /**
24374      * This method is only used by a DataProxy which has retrieved data from a remote server.
24375          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24376          * to contain a method called 'responseXML' that returns an XML document object.
24377      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24378      * a cache of Roo.data.Records.
24379      */
24380     read : function(response){
24381         var doc = response.responseXML;
24382         if(!doc) {
24383             throw {message: "XmlReader.read: XML Document not available"};
24384         }
24385         return this.readRecords(doc);
24386     },
24387
24388     /**
24389      * Create a data block containing Roo.data.Records from an XML document.
24390          * @param {Object} doc A parsed XML document.
24391      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24392      * a cache of Roo.data.Records.
24393      */
24394     readRecords : function(doc){
24395         /**
24396          * After any data loads/reads, the raw XML Document is available for further custom processing.
24397          * @type XMLDocument
24398          */
24399         this.xmlData = doc;
24400         var root = doc.documentElement || doc;
24401         var q = Roo.DomQuery;
24402         var recordType = this.recordType, fields = recordType.prototype.fields;
24403         var sid = this.meta.id;
24404         var totalRecords = 0, success = true;
24405         if(this.meta.totalRecords){
24406             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24407         }
24408         
24409         if(this.meta.success){
24410             var sv = q.selectValue(this.meta.success, root, true);
24411             success = sv !== false && sv !== 'false';
24412         }
24413         var records = [];
24414         var ns = q.select(this.meta.record, root);
24415         for(var i = 0, len = ns.length; i < len; i++) {
24416                 var n = ns[i];
24417                 var values = {};
24418                 var id = sid ? q.selectValue(sid, n) : undefined;
24419                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24420                     var f = fields.items[j];
24421                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24422                     v = f.convert(v);
24423                     values[f.name] = v;
24424                 }
24425                 var record = new recordType(values, id);
24426                 record.node = n;
24427                 records[records.length] = record;
24428             }
24429
24430             return {
24431                 success : success,
24432                 records : records,
24433                 totalRecords : totalRecords || records.length
24434             };
24435     }
24436 });/*
24437  * Based on:
24438  * Ext JS Library 1.1.1
24439  * Copyright(c) 2006-2007, Ext JS, LLC.
24440  *
24441  * Originally Released Under LGPL - original licence link has changed is not relivant.
24442  *
24443  * Fork - LGPL
24444  * <script type="text/javascript">
24445  */
24446
24447 /**
24448  * @class Roo.data.ArrayReader
24449  * @extends Roo.data.DataReader
24450  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24451  * Each element of that Array represents a row of data fields. The
24452  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24453  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24454  * <p>
24455  * Example code:.
24456  * <pre><code>
24457 var RecordDef = Roo.data.Record.create([
24458     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24459     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24460 ]);
24461 var myReader = new Roo.data.ArrayReader({
24462     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24463 }, RecordDef);
24464 </code></pre>
24465  * <p>
24466  * This would consume an Array like this:
24467  * <pre><code>
24468 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24469   </code></pre>
24470  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24471  * @constructor
24472  * Create a new JsonReader
24473  * @param {Object} meta Metadata configuration options.
24474  * @param {Object} recordType Either an Array of field definition objects
24475  * as specified to {@link Roo.data.Record#create},
24476  * or an {@link Roo.data.Record} object
24477  * created using {@link Roo.data.Record#create}.
24478  */
24479 Roo.data.ArrayReader = function(meta, recordType){
24480     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24481 };
24482
24483 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24484     /**
24485      * Create a data block containing Roo.data.Records from an XML document.
24486      * @param {Object} o An Array of row objects which represents the dataset.
24487      * @return {Object} data A data block which is used by an Roo.data.Store object as
24488      * a cache of Roo.data.Records.
24489      */
24490     readRecords : function(o){
24491         var sid = this.meta ? this.meta.id : null;
24492         var recordType = this.recordType, fields = recordType.prototype.fields;
24493         var records = [];
24494         var root = o;
24495             for(var i = 0; i < root.length; i++){
24496                     var n = root[i];
24497                 var values = {};
24498                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24499                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24500                 var f = fields.items[j];
24501                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24502                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24503                 v = f.convert(v);
24504                 values[f.name] = v;
24505             }
24506                 var record = new recordType(values, id);
24507                 record.json = n;
24508                 records[records.length] = record;
24509             }
24510             return {
24511                 records : records,
24512                 totalRecords : records.length
24513             };
24514     }
24515 });/*
24516  * Based on:
24517  * Ext JS Library 1.1.1
24518  * Copyright(c) 2006-2007, Ext JS, LLC.
24519  *
24520  * Originally Released Under LGPL - original licence link has changed is not relivant.
24521  *
24522  * Fork - LGPL
24523  * <script type="text/javascript">
24524  */
24525
24526
24527 /**
24528  * @class Roo.data.Tree
24529  * @extends Roo.util.Observable
24530  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24531  * in the tree have most standard DOM functionality.
24532  * @constructor
24533  * @param {Node} root (optional) The root node
24534  */
24535 Roo.data.Tree = function(root){
24536    this.nodeHash = {};
24537    /**
24538     * The root node for this tree
24539     * @type Node
24540     */
24541    this.root = null;
24542    if(root){
24543        this.setRootNode(root);
24544    }
24545    this.addEvents({
24546        /**
24547         * @event append
24548         * Fires when a new child node is appended to a node in this tree.
24549         * @param {Tree} tree The owner tree
24550         * @param {Node} parent The parent node
24551         * @param {Node} node The newly appended node
24552         * @param {Number} index The index of the newly appended node
24553         */
24554        "append" : true,
24555        /**
24556         * @event remove
24557         * Fires when a child node is removed from a node in this tree.
24558         * @param {Tree} tree The owner tree
24559         * @param {Node} parent The parent node
24560         * @param {Node} node The child node removed
24561         */
24562        "remove" : true,
24563        /**
24564         * @event move
24565         * Fires when a node is moved to a new location in the tree
24566         * @param {Tree} tree The owner tree
24567         * @param {Node} node The node moved
24568         * @param {Node} oldParent The old parent of this node
24569         * @param {Node} newParent The new parent of this node
24570         * @param {Number} index The index it was moved to
24571         */
24572        "move" : true,
24573        /**
24574         * @event insert
24575         * Fires when a new child node is inserted in a node in this tree.
24576         * @param {Tree} tree The owner tree
24577         * @param {Node} parent The parent node
24578         * @param {Node} node The child node inserted
24579         * @param {Node} refNode The child node the node was inserted before
24580         */
24581        "insert" : true,
24582        /**
24583         * @event beforeappend
24584         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24585         * @param {Tree} tree The owner tree
24586         * @param {Node} parent The parent node
24587         * @param {Node} node The child node to be appended
24588         */
24589        "beforeappend" : true,
24590        /**
24591         * @event beforeremove
24592         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24593         * @param {Tree} tree The owner tree
24594         * @param {Node} parent The parent node
24595         * @param {Node} node The child node to be removed
24596         */
24597        "beforeremove" : true,
24598        /**
24599         * @event beforemove
24600         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24601         * @param {Tree} tree The owner tree
24602         * @param {Node} node The node being moved
24603         * @param {Node} oldParent The parent of the node
24604         * @param {Node} newParent The new parent the node is moving to
24605         * @param {Number} index The index it is being moved to
24606         */
24607        "beforemove" : true,
24608        /**
24609         * @event beforeinsert
24610         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24611         * @param {Tree} tree The owner tree
24612         * @param {Node} parent The parent node
24613         * @param {Node} node The child node to be inserted
24614         * @param {Node} refNode The child node the node is being inserted before
24615         */
24616        "beforeinsert" : true
24617    });
24618
24619     Roo.data.Tree.superclass.constructor.call(this);
24620 };
24621
24622 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24623     pathSeparator: "/",
24624
24625     proxyNodeEvent : function(){
24626         return this.fireEvent.apply(this, arguments);
24627     },
24628
24629     /**
24630      * Returns the root node for this tree.
24631      * @return {Node}
24632      */
24633     getRootNode : function(){
24634         return this.root;
24635     },
24636
24637     /**
24638      * Sets the root node for this tree.
24639      * @param {Node} node
24640      * @return {Node}
24641      */
24642     setRootNode : function(node){
24643         this.root = node;
24644         node.ownerTree = this;
24645         node.isRoot = true;
24646         this.registerNode(node);
24647         return node;
24648     },
24649
24650     /**
24651      * Gets a node in this tree by its id.
24652      * @param {String} id
24653      * @return {Node}
24654      */
24655     getNodeById : function(id){
24656         return this.nodeHash[id];
24657     },
24658
24659     registerNode : function(node){
24660         this.nodeHash[node.id] = node;
24661     },
24662
24663     unregisterNode : function(node){
24664         delete this.nodeHash[node.id];
24665     },
24666
24667     toString : function(){
24668         return "[Tree"+(this.id?" "+this.id:"")+"]";
24669     }
24670 });
24671
24672 /**
24673  * @class Roo.data.Node
24674  * @extends Roo.util.Observable
24675  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24676  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24677  * @constructor
24678  * @param {Object} attributes The attributes/config for the node
24679  */
24680 Roo.data.Node = function(attributes){
24681     /**
24682      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24683      * @type {Object}
24684      */
24685     this.attributes = attributes || {};
24686     this.leaf = this.attributes.leaf;
24687     /**
24688      * The node id. @type String
24689      */
24690     this.id = this.attributes.id;
24691     if(!this.id){
24692         this.id = Roo.id(null, "ynode-");
24693         this.attributes.id = this.id;
24694     }
24695      
24696     
24697     /**
24698      * All child nodes of this node. @type Array
24699      */
24700     this.childNodes = [];
24701     if(!this.childNodes.indexOf){ // indexOf is a must
24702         this.childNodes.indexOf = function(o){
24703             for(var i = 0, len = this.length; i < len; i++){
24704                 if(this[i] == o) {
24705                     return i;
24706                 }
24707             }
24708             return -1;
24709         };
24710     }
24711     /**
24712      * The parent node for this node. @type Node
24713      */
24714     this.parentNode = null;
24715     /**
24716      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24717      */
24718     this.firstChild = null;
24719     /**
24720      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24721      */
24722     this.lastChild = null;
24723     /**
24724      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24725      */
24726     this.previousSibling = null;
24727     /**
24728      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24729      */
24730     this.nextSibling = null;
24731
24732     this.addEvents({
24733        /**
24734         * @event append
24735         * Fires when a new child node is appended
24736         * @param {Tree} tree The owner tree
24737         * @param {Node} this This node
24738         * @param {Node} node The newly appended node
24739         * @param {Number} index The index of the newly appended node
24740         */
24741        "append" : true,
24742        /**
24743         * @event remove
24744         * Fires when a child node is removed
24745         * @param {Tree} tree The owner tree
24746         * @param {Node} this This node
24747         * @param {Node} node The removed node
24748         */
24749        "remove" : true,
24750        /**
24751         * @event move
24752         * Fires when this node is moved to a new location in the tree
24753         * @param {Tree} tree The owner tree
24754         * @param {Node} this This node
24755         * @param {Node} oldParent The old parent of this node
24756         * @param {Node} newParent The new parent of this node
24757         * @param {Number} index The index it was moved to
24758         */
24759        "move" : true,
24760        /**
24761         * @event insert
24762         * Fires when a new child node is inserted.
24763         * @param {Tree} tree The owner tree
24764         * @param {Node} this This node
24765         * @param {Node} node The child node inserted
24766         * @param {Node} refNode The child node the node was inserted before
24767         */
24768        "insert" : true,
24769        /**
24770         * @event beforeappend
24771         * Fires before a new child is appended, return false to cancel the append.
24772         * @param {Tree} tree The owner tree
24773         * @param {Node} this This node
24774         * @param {Node} node The child node to be appended
24775         */
24776        "beforeappend" : true,
24777        /**
24778         * @event beforeremove
24779         * Fires before a child is removed, return false to cancel the remove.
24780         * @param {Tree} tree The owner tree
24781         * @param {Node} this This node
24782         * @param {Node} node The child node to be removed
24783         */
24784        "beforeremove" : true,
24785        /**
24786         * @event beforemove
24787         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24788         * @param {Tree} tree The owner tree
24789         * @param {Node} this This node
24790         * @param {Node} oldParent The parent of this node
24791         * @param {Node} newParent The new parent this node is moving to
24792         * @param {Number} index The index it is being moved to
24793         */
24794        "beforemove" : true,
24795        /**
24796         * @event beforeinsert
24797         * Fires before a new child is inserted, return false to cancel the insert.
24798         * @param {Tree} tree The owner tree
24799         * @param {Node} this This node
24800         * @param {Node} node The child node to be inserted
24801         * @param {Node} refNode The child node the node is being inserted before
24802         */
24803        "beforeinsert" : true
24804    });
24805     this.listeners = this.attributes.listeners;
24806     Roo.data.Node.superclass.constructor.call(this);
24807 };
24808
24809 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24810     fireEvent : function(evtName){
24811         // first do standard event for this node
24812         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24813             return false;
24814         }
24815         // then bubble it up to the tree if the event wasn't cancelled
24816         var ot = this.getOwnerTree();
24817         if(ot){
24818             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24819                 return false;
24820             }
24821         }
24822         return true;
24823     },
24824
24825     /**
24826      * Returns true if this node is a leaf
24827      * @return {Boolean}
24828      */
24829     isLeaf : function(){
24830         return this.leaf === true;
24831     },
24832
24833     // private
24834     setFirstChild : function(node){
24835         this.firstChild = node;
24836     },
24837
24838     //private
24839     setLastChild : function(node){
24840         this.lastChild = node;
24841     },
24842
24843
24844     /**
24845      * Returns true if this node is the last child of its parent
24846      * @return {Boolean}
24847      */
24848     isLast : function(){
24849        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24850     },
24851
24852     /**
24853      * Returns true if this node is the first child of its parent
24854      * @return {Boolean}
24855      */
24856     isFirst : function(){
24857        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24858     },
24859
24860     hasChildNodes : function(){
24861         return !this.isLeaf() && this.childNodes.length > 0;
24862     },
24863
24864     /**
24865      * Insert node(s) as the last child node of this node.
24866      * @param {Node/Array} node The node or Array of nodes to append
24867      * @return {Node} The appended node if single append, or null if an array was passed
24868      */
24869     appendChild : function(node){
24870         var multi = false;
24871         if(node instanceof Array){
24872             multi = node;
24873         }else if(arguments.length > 1){
24874             multi = arguments;
24875         }
24876         // if passed an array or multiple args do them one by one
24877         if(multi){
24878             for(var i = 0, len = multi.length; i < len; i++) {
24879                 this.appendChild(multi[i]);
24880             }
24881         }else{
24882             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24883                 return false;
24884             }
24885             var index = this.childNodes.length;
24886             var oldParent = node.parentNode;
24887             // it's a move, make sure we move it cleanly
24888             if(oldParent){
24889                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24890                     return false;
24891                 }
24892                 oldParent.removeChild(node);
24893             }
24894             index = this.childNodes.length;
24895             if(index == 0){
24896                 this.setFirstChild(node);
24897             }
24898             this.childNodes.push(node);
24899             node.parentNode = this;
24900             var ps = this.childNodes[index-1];
24901             if(ps){
24902                 node.previousSibling = ps;
24903                 ps.nextSibling = node;
24904             }else{
24905                 node.previousSibling = null;
24906             }
24907             node.nextSibling = null;
24908             this.setLastChild(node);
24909             node.setOwnerTree(this.getOwnerTree());
24910             this.fireEvent("append", this.ownerTree, this, node, index);
24911             if(oldParent){
24912                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24913             }
24914             return node;
24915         }
24916     },
24917
24918     /**
24919      * Removes a child node from this node.
24920      * @param {Node} node The node to remove
24921      * @return {Node} The removed node
24922      */
24923     removeChild : function(node){
24924         var index = this.childNodes.indexOf(node);
24925         if(index == -1){
24926             return false;
24927         }
24928         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24929             return false;
24930         }
24931
24932         // remove it from childNodes collection
24933         this.childNodes.splice(index, 1);
24934
24935         // update siblings
24936         if(node.previousSibling){
24937             node.previousSibling.nextSibling = node.nextSibling;
24938         }
24939         if(node.nextSibling){
24940             node.nextSibling.previousSibling = node.previousSibling;
24941         }
24942
24943         // update child refs
24944         if(this.firstChild == node){
24945             this.setFirstChild(node.nextSibling);
24946         }
24947         if(this.lastChild == node){
24948             this.setLastChild(node.previousSibling);
24949         }
24950
24951         node.setOwnerTree(null);
24952         // clear any references from the node
24953         node.parentNode = null;
24954         node.previousSibling = null;
24955         node.nextSibling = null;
24956         this.fireEvent("remove", this.ownerTree, this, node);
24957         return node;
24958     },
24959
24960     /**
24961      * Inserts the first node before the second node in this nodes childNodes collection.
24962      * @param {Node} node The node to insert
24963      * @param {Node} refNode The node to insert before (if null the node is appended)
24964      * @return {Node} The inserted node
24965      */
24966     insertBefore : function(node, refNode){
24967         if(!refNode){ // like standard Dom, refNode can be null for append
24968             return this.appendChild(node);
24969         }
24970         // nothing to do
24971         if(node == refNode){
24972             return false;
24973         }
24974
24975         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24976             return false;
24977         }
24978         var index = this.childNodes.indexOf(refNode);
24979         var oldParent = node.parentNode;
24980         var refIndex = index;
24981
24982         // when moving internally, indexes will change after remove
24983         if(oldParent == this && this.childNodes.indexOf(node) < index){
24984             refIndex--;
24985         }
24986
24987         // it's a move, make sure we move it cleanly
24988         if(oldParent){
24989             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24990                 return false;
24991             }
24992             oldParent.removeChild(node);
24993         }
24994         if(refIndex == 0){
24995             this.setFirstChild(node);
24996         }
24997         this.childNodes.splice(refIndex, 0, node);
24998         node.parentNode = this;
24999         var ps = this.childNodes[refIndex-1];
25000         if(ps){
25001             node.previousSibling = ps;
25002             ps.nextSibling = node;
25003         }else{
25004             node.previousSibling = null;
25005         }
25006         node.nextSibling = refNode;
25007         refNode.previousSibling = node;
25008         node.setOwnerTree(this.getOwnerTree());
25009         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25010         if(oldParent){
25011             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25012         }
25013         return node;
25014     },
25015
25016     /**
25017      * Returns the child node at the specified index.
25018      * @param {Number} index
25019      * @return {Node}
25020      */
25021     item : function(index){
25022         return this.childNodes[index];
25023     },
25024
25025     /**
25026      * Replaces one child node in this node with another.
25027      * @param {Node} newChild The replacement node
25028      * @param {Node} oldChild The node to replace
25029      * @return {Node} The replaced node
25030      */
25031     replaceChild : function(newChild, oldChild){
25032         this.insertBefore(newChild, oldChild);
25033         this.removeChild(oldChild);
25034         return oldChild;
25035     },
25036
25037     /**
25038      * Returns the index of a child node
25039      * @param {Node} node
25040      * @return {Number} The index of the node or -1 if it was not found
25041      */
25042     indexOf : function(child){
25043         return this.childNodes.indexOf(child);
25044     },
25045
25046     /**
25047      * Returns the tree this node is in.
25048      * @return {Tree}
25049      */
25050     getOwnerTree : function(){
25051         // if it doesn't have one, look for one
25052         if(!this.ownerTree){
25053             var p = this;
25054             while(p){
25055                 if(p.ownerTree){
25056                     this.ownerTree = p.ownerTree;
25057                     break;
25058                 }
25059                 p = p.parentNode;
25060             }
25061         }
25062         return this.ownerTree;
25063     },
25064
25065     /**
25066      * Returns depth of this node (the root node has a depth of 0)
25067      * @return {Number}
25068      */
25069     getDepth : function(){
25070         var depth = 0;
25071         var p = this;
25072         while(p.parentNode){
25073             ++depth;
25074             p = p.parentNode;
25075         }
25076         return depth;
25077     },
25078
25079     // private
25080     setOwnerTree : function(tree){
25081         // if it's move, we need to update everyone
25082         if(tree != this.ownerTree){
25083             if(this.ownerTree){
25084                 this.ownerTree.unregisterNode(this);
25085             }
25086             this.ownerTree = tree;
25087             var cs = this.childNodes;
25088             for(var i = 0, len = cs.length; i < len; i++) {
25089                 cs[i].setOwnerTree(tree);
25090             }
25091             if(tree){
25092                 tree.registerNode(this);
25093             }
25094         }
25095     },
25096
25097     /**
25098      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25099      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25100      * @return {String} The path
25101      */
25102     getPath : function(attr){
25103         attr = attr || "id";
25104         var p = this.parentNode;
25105         var b = [this.attributes[attr]];
25106         while(p){
25107             b.unshift(p.attributes[attr]);
25108             p = p.parentNode;
25109         }
25110         var sep = this.getOwnerTree().pathSeparator;
25111         return sep + b.join(sep);
25112     },
25113
25114     /**
25115      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25116      * function call will be the scope provided or the current node. The arguments to the function
25117      * will be the args provided or the current node. If the function returns false at any point,
25118      * the bubble is stopped.
25119      * @param {Function} fn The function to call
25120      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25121      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25122      */
25123     bubble : function(fn, scope, args){
25124         var p = this;
25125         while(p){
25126             if(fn.call(scope || p, args || p) === false){
25127                 break;
25128             }
25129             p = p.parentNode;
25130         }
25131     },
25132
25133     /**
25134      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25135      * function call will be the scope provided or the current node. The arguments to the function
25136      * will be the args provided or the current node. If the function returns false at any point,
25137      * the cascade is stopped on that branch.
25138      * @param {Function} fn The function to call
25139      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25140      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25141      */
25142     cascade : function(fn, scope, args){
25143         if(fn.call(scope || this, args || this) !== false){
25144             var cs = this.childNodes;
25145             for(var i = 0, len = cs.length; i < len; i++) {
25146                 cs[i].cascade(fn, scope, args);
25147             }
25148         }
25149     },
25150
25151     /**
25152      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25153      * function call will be the scope provided or the current node. The arguments to the function
25154      * will be the args provided or the current node. If the function returns false at any point,
25155      * the iteration stops.
25156      * @param {Function} fn The function to call
25157      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25158      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25159      */
25160     eachChild : function(fn, scope, args){
25161         var cs = this.childNodes;
25162         for(var i = 0, len = cs.length; i < len; i++) {
25163                 if(fn.call(scope || this, args || cs[i]) === false){
25164                     break;
25165                 }
25166         }
25167     },
25168
25169     /**
25170      * Finds the first child that has the attribute with the specified value.
25171      * @param {String} attribute The attribute name
25172      * @param {Mixed} value The value to search for
25173      * @return {Node} The found child or null if none was found
25174      */
25175     findChild : function(attribute, value){
25176         var cs = this.childNodes;
25177         for(var i = 0, len = cs.length; i < len; i++) {
25178                 if(cs[i].attributes[attribute] == value){
25179                     return cs[i];
25180                 }
25181         }
25182         return null;
25183     },
25184
25185     /**
25186      * Finds the first child by a custom function. The child matches if the function passed
25187      * returns true.
25188      * @param {Function} fn
25189      * @param {Object} scope (optional)
25190      * @return {Node} The found child or null if none was found
25191      */
25192     findChildBy : function(fn, scope){
25193         var cs = this.childNodes;
25194         for(var i = 0, len = cs.length; i < len; i++) {
25195                 if(fn.call(scope||cs[i], cs[i]) === true){
25196                     return cs[i];
25197                 }
25198         }
25199         return null;
25200     },
25201
25202     /**
25203      * Sorts this nodes children using the supplied sort function
25204      * @param {Function} fn
25205      * @param {Object} scope (optional)
25206      */
25207     sort : function(fn, scope){
25208         var cs = this.childNodes;
25209         var len = cs.length;
25210         if(len > 0){
25211             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25212             cs.sort(sortFn);
25213             for(var i = 0; i < len; i++){
25214                 var n = cs[i];
25215                 n.previousSibling = cs[i-1];
25216                 n.nextSibling = cs[i+1];
25217                 if(i == 0){
25218                     this.setFirstChild(n);
25219                 }
25220                 if(i == len-1){
25221                     this.setLastChild(n);
25222                 }
25223             }
25224         }
25225     },
25226
25227     /**
25228      * Returns true if this node is an ancestor (at any point) of the passed node.
25229      * @param {Node} node
25230      * @return {Boolean}
25231      */
25232     contains : function(node){
25233         return node.isAncestor(this);
25234     },
25235
25236     /**
25237      * Returns true if the passed node is an ancestor (at any point) of this node.
25238      * @param {Node} node
25239      * @return {Boolean}
25240      */
25241     isAncestor : function(node){
25242         var p = this.parentNode;
25243         while(p){
25244             if(p == node){
25245                 return true;
25246             }
25247             p = p.parentNode;
25248         }
25249         return false;
25250     },
25251
25252     toString : function(){
25253         return "[Node"+(this.id?" "+this.id:"")+"]";
25254     }
25255 });/*
25256  * Based on:
25257  * Ext JS Library 1.1.1
25258  * Copyright(c) 2006-2007, Ext JS, LLC.
25259  *
25260  * Originally Released Under LGPL - original licence link has changed is not relivant.
25261  *
25262  * Fork - LGPL
25263  * <script type="text/javascript">
25264  */
25265  (function(){ 
25266 /**
25267  * @class Roo.Layer
25268  * @extends Roo.Element
25269  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25270  * automatic maintaining of shadow/shim positions.
25271  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25272  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25273  * you can pass a string with a CSS class name. False turns off the shadow.
25274  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25275  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25276  * @cfg {String} cls CSS class to add to the element
25277  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25278  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25279  * @constructor
25280  * @param {Object} config An object with config options.
25281  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25282  */
25283
25284 Roo.Layer = function(config, existingEl){
25285     config = config || {};
25286     var dh = Roo.DomHelper;
25287     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25288     if(existingEl){
25289         this.dom = Roo.getDom(existingEl);
25290     }
25291     if(!this.dom){
25292         var o = config.dh || {tag: "div", cls: "x-layer"};
25293         this.dom = dh.append(pel, o);
25294     }
25295     if(config.cls){
25296         this.addClass(config.cls);
25297     }
25298     this.constrain = config.constrain !== false;
25299     this.visibilityMode = Roo.Element.VISIBILITY;
25300     if(config.id){
25301         this.id = this.dom.id = config.id;
25302     }else{
25303         this.id = Roo.id(this.dom);
25304     }
25305     this.zindex = config.zindex || this.getZIndex();
25306     this.position("absolute", this.zindex);
25307     if(config.shadow){
25308         this.shadowOffset = config.shadowOffset || 4;
25309         this.shadow = new Roo.Shadow({
25310             offset : this.shadowOffset,
25311             mode : config.shadow
25312         });
25313     }else{
25314         this.shadowOffset = 0;
25315     }
25316     this.useShim = config.shim !== false && Roo.useShims;
25317     this.useDisplay = config.useDisplay;
25318     this.hide();
25319 };
25320
25321 var supr = Roo.Element.prototype;
25322
25323 // shims are shared among layer to keep from having 100 iframes
25324 var shims = [];
25325
25326 Roo.extend(Roo.Layer, Roo.Element, {
25327
25328     getZIndex : function(){
25329         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25330     },
25331
25332     getShim : function(){
25333         if(!this.useShim){
25334             return null;
25335         }
25336         if(this.shim){
25337             return this.shim;
25338         }
25339         var shim = shims.shift();
25340         if(!shim){
25341             shim = this.createShim();
25342             shim.enableDisplayMode('block');
25343             shim.dom.style.display = 'none';
25344             shim.dom.style.visibility = 'visible';
25345         }
25346         var pn = this.dom.parentNode;
25347         if(shim.dom.parentNode != pn){
25348             pn.insertBefore(shim.dom, this.dom);
25349         }
25350         shim.setStyle('z-index', this.getZIndex()-2);
25351         this.shim = shim;
25352         return shim;
25353     },
25354
25355     hideShim : function(){
25356         if(this.shim){
25357             this.shim.setDisplayed(false);
25358             shims.push(this.shim);
25359             delete this.shim;
25360         }
25361     },
25362
25363     disableShadow : function(){
25364         if(this.shadow){
25365             this.shadowDisabled = true;
25366             this.shadow.hide();
25367             this.lastShadowOffset = this.shadowOffset;
25368             this.shadowOffset = 0;
25369         }
25370     },
25371
25372     enableShadow : function(show){
25373         if(this.shadow){
25374             this.shadowDisabled = false;
25375             this.shadowOffset = this.lastShadowOffset;
25376             delete this.lastShadowOffset;
25377             if(show){
25378                 this.sync(true);
25379             }
25380         }
25381     },
25382
25383     // private
25384     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25385     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25386     sync : function(doShow){
25387         var sw = this.shadow;
25388         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25389             var sh = this.getShim();
25390
25391             var w = this.getWidth(),
25392                 h = this.getHeight();
25393
25394             var l = this.getLeft(true),
25395                 t = this.getTop(true);
25396
25397             if(sw && !this.shadowDisabled){
25398                 if(doShow && !sw.isVisible()){
25399                     sw.show(this);
25400                 }else{
25401                     sw.realign(l, t, w, h);
25402                 }
25403                 if(sh){
25404                     if(doShow){
25405                        sh.show();
25406                     }
25407                     // fit the shim behind the shadow, so it is shimmed too
25408                     var a = sw.adjusts, s = sh.dom.style;
25409                     s.left = (Math.min(l, l+a.l))+"px";
25410                     s.top = (Math.min(t, t+a.t))+"px";
25411                     s.width = (w+a.w)+"px";
25412                     s.height = (h+a.h)+"px";
25413                 }
25414             }else if(sh){
25415                 if(doShow){
25416                    sh.show();
25417                 }
25418                 sh.setSize(w, h);
25419                 sh.setLeftTop(l, t);
25420             }
25421             
25422         }
25423     },
25424
25425     // private
25426     destroy : function(){
25427         this.hideShim();
25428         if(this.shadow){
25429             this.shadow.hide();
25430         }
25431         this.removeAllListeners();
25432         var pn = this.dom.parentNode;
25433         if(pn){
25434             pn.removeChild(this.dom);
25435         }
25436         Roo.Element.uncache(this.id);
25437     },
25438
25439     remove : function(){
25440         this.destroy();
25441     },
25442
25443     // private
25444     beginUpdate : function(){
25445         this.updating = true;
25446     },
25447
25448     // private
25449     endUpdate : function(){
25450         this.updating = false;
25451         this.sync(true);
25452     },
25453
25454     // private
25455     hideUnders : function(negOffset){
25456         if(this.shadow){
25457             this.shadow.hide();
25458         }
25459         this.hideShim();
25460     },
25461
25462     // private
25463     constrainXY : function(){
25464         if(this.constrain){
25465             var vw = Roo.lib.Dom.getViewWidth(),
25466                 vh = Roo.lib.Dom.getViewHeight();
25467             var s = Roo.get(document).getScroll();
25468
25469             var xy = this.getXY();
25470             var x = xy[0], y = xy[1];   
25471             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25472             // only move it if it needs it
25473             var moved = false;
25474             // first validate right/bottom
25475             if((x + w) > vw+s.left){
25476                 x = vw - w - this.shadowOffset;
25477                 moved = true;
25478             }
25479             if((y + h) > vh+s.top){
25480                 y = vh - h - this.shadowOffset;
25481                 moved = true;
25482             }
25483             // then make sure top/left isn't negative
25484             if(x < s.left){
25485                 x = s.left;
25486                 moved = true;
25487             }
25488             if(y < s.top){
25489                 y = s.top;
25490                 moved = true;
25491             }
25492             if(moved){
25493                 if(this.avoidY){
25494                     var ay = this.avoidY;
25495                     if(y <= ay && (y+h) >= ay){
25496                         y = ay-h-5;   
25497                     }
25498                 }
25499                 xy = [x, y];
25500                 this.storeXY(xy);
25501                 supr.setXY.call(this, xy);
25502                 this.sync();
25503             }
25504         }
25505     },
25506
25507     isVisible : function(){
25508         return this.visible;    
25509     },
25510
25511     // private
25512     showAction : function(){
25513         this.visible = true; // track visibility to prevent getStyle calls
25514         if(this.useDisplay === true){
25515             this.setDisplayed("");
25516         }else if(this.lastXY){
25517             supr.setXY.call(this, this.lastXY);
25518         }else if(this.lastLT){
25519             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25520         }
25521     },
25522
25523     // private
25524     hideAction : function(){
25525         this.visible = false;
25526         if(this.useDisplay === true){
25527             this.setDisplayed(false);
25528         }else{
25529             this.setLeftTop(-10000,-10000);
25530         }
25531     },
25532
25533     // overridden Element method
25534     setVisible : function(v, a, d, c, e){
25535         if(v){
25536             this.showAction();
25537         }
25538         if(a && v){
25539             var cb = function(){
25540                 this.sync(true);
25541                 if(c){
25542                     c();
25543                 }
25544             }.createDelegate(this);
25545             supr.setVisible.call(this, true, true, d, cb, e);
25546         }else{
25547             if(!v){
25548                 this.hideUnders(true);
25549             }
25550             var cb = c;
25551             if(a){
25552                 cb = function(){
25553                     this.hideAction();
25554                     if(c){
25555                         c();
25556                     }
25557                 }.createDelegate(this);
25558             }
25559             supr.setVisible.call(this, v, a, d, cb, e);
25560             if(v){
25561                 this.sync(true);
25562             }else if(!a){
25563                 this.hideAction();
25564             }
25565         }
25566     },
25567
25568     storeXY : function(xy){
25569         delete this.lastLT;
25570         this.lastXY = xy;
25571     },
25572
25573     storeLeftTop : function(left, top){
25574         delete this.lastXY;
25575         this.lastLT = [left, top];
25576     },
25577
25578     // private
25579     beforeFx : function(){
25580         this.beforeAction();
25581         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25582     },
25583
25584     // private
25585     afterFx : function(){
25586         Roo.Layer.superclass.afterFx.apply(this, arguments);
25587         this.sync(this.isVisible());
25588     },
25589
25590     // private
25591     beforeAction : function(){
25592         if(!this.updating && this.shadow){
25593             this.shadow.hide();
25594         }
25595     },
25596
25597     // overridden Element method
25598     setLeft : function(left){
25599         this.storeLeftTop(left, this.getTop(true));
25600         supr.setLeft.apply(this, arguments);
25601         this.sync();
25602     },
25603
25604     setTop : function(top){
25605         this.storeLeftTop(this.getLeft(true), top);
25606         supr.setTop.apply(this, arguments);
25607         this.sync();
25608     },
25609
25610     setLeftTop : function(left, top){
25611         this.storeLeftTop(left, top);
25612         supr.setLeftTop.apply(this, arguments);
25613         this.sync();
25614     },
25615
25616     setXY : function(xy, a, d, c, e){
25617         this.fixDisplay();
25618         this.beforeAction();
25619         this.storeXY(xy);
25620         var cb = this.createCB(c);
25621         supr.setXY.call(this, xy, a, d, cb, e);
25622         if(!a){
25623             cb();
25624         }
25625     },
25626
25627     // private
25628     createCB : function(c){
25629         var el = this;
25630         return function(){
25631             el.constrainXY();
25632             el.sync(true);
25633             if(c){
25634                 c();
25635             }
25636         };
25637     },
25638
25639     // overridden Element method
25640     setX : function(x, a, d, c, e){
25641         this.setXY([x, this.getY()], a, d, c, e);
25642     },
25643
25644     // overridden Element method
25645     setY : function(y, a, d, c, e){
25646         this.setXY([this.getX(), y], a, d, c, e);
25647     },
25648
25649     // overridden Element method
25650     setSize : function(w, h, a, d, c, e){
25651         this.beforeAction();
25652         var cb = this.createCB(c);
25653         supr.setSize.call(this, w, h, a, d, cb, e);
25654         if(!a){
25655             cb();
25656         }
25657     },
25658
25659     // overridden Element method
25660     setWidth : function(w, a, d, c, e){
25661         this.beforeAction();
25662         var cb = this.createCB(c);
25663         supr.setWidth.call(this, w, a, d, cb, e);
25664         if(!a){
25665             cb();
25666         }
25667     },
25668
25669     // overridden Element method
25670     setHeight : function(h, a, d, c, e){
25671         this.beforeAction();
25672         var cb = this.createCB(c);
25673         supr.setHeight.call(this, h, a, d, cb, e);
25674         if(!a){
25675             cb();
25676         }
25677     },
25678
25679     // overridden Element method
25680     setBounds : function(x, y, w, h, a, d, c, e){
25681         this.beforeAction();
25682         var cb = this.createCB(c);
25683         if(!a){
25684             this.storeXY([x, y]);
25685             supr.setXY.call(this, [x, y]);
25686             supr.setSize.call(this, w, h, a, d, cb, e);
25687             cb();
25688         }else{
25689             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25690         }
25691         return this;
25692     },
25693     
25694     /**
25695      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25696      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25697      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25698      * @param {Number} zindex The new z-index to set
25699      * @return {this} The Layer
25700      */
25701     setZIndex : function(zindex){
25702         this.zindex = zindex;
25703         this.setStyle("z-index", zindex + 2);
25704         if(this.shadow){
25705             this.shadow.setZIndex(zindex + 1);
25706         }
25707         if(this.shim){
25708             this.shim.setStyle("z-index", zindex);
25709         }
25710     }
25711 });
25712 })();/*
25713  * Based on:
25714  * Ext JS Library 1.1.1
25715  * Copyright(c) 2006-2007, Ext JS, LLC.
25716  *
25717  * Originally Released Under LGPL - original licence link has changed is not relivant.
25718  *
25719  * Fork - LGPL
25720  * <script type="text/javascript">
25721  */
25722
25723
25724 /**
25725  * @class Roo.Shadow
25726  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25727  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25728  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25729  * @constructor
25730  * Create a new Shadow
25731  * @param {Object} config The config object
25732  */
25733 Roo.Shadow = function(config){
25734     Roo.apply(this, config);
25735     if(typeof this.mode != "string"){
25736         this.mode = this.defaultMode;
25737     }
25738     var o = this.offset, a = {h: 0};
25739     var rad = Math.floor(this.offset/2);
25740     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25741         case "drop":
25742             a.w = 0;
25743             a.l = a.t = o;
25744             a.t -= 1;
25745             if(Roo.isIE){
25746                 a.l -= this.offset + rad;
25747                 a.t -= this.offset + rad;
25748                 a.w -= rad;
25749                 a.h -= rad;
25750                 a.t += 1;
25751             }
25752         break;
25753         case "sides":
25754             a.w = (o*2);
25755             a.l = -o;
25756             a.t = o-1;
25757             if(Roo.isIE){
25758                 a.l -= (this.offset - rad);
25759                 a.t -= this.offset + rad;
25760                 a.l += 1;
25761                 a.w -= (this.offset - rad)*2;
25762                 a.w -= rad + 1;
25763                 a.h -= 1;
25764             }
25765         break;
25766         case "frame":
25767             a.w = a.h = (o*2);
25768             a.l = a.t = -o;
25769             a.t += 1;
25770             a.h -= 2;
25771             if(Roo.isIE){
25772                 a.l -= (this.offset - rad);
25773                 a.t -= (this.offset - rad);
25774                 a.l += 1;
25775                 a.w -= (this.offset + rad + 1);
25776                 a.h -= (this.offset + rad);
25777                 a.h += 1;
25778             }
25779         break;
25780     };
25781
25782     this.adjusts = a;
25783 };
25784
25785 Roo.Shadow.prototype = {
25786     /**
25787      * @cfg {String} mode
25788      * The shadow display mode.  Supports the following options:<br />
25789      * sides: Shadow displays on both sides and bottom only<br />
25790      * frame: Shadow displays equally on all four sides<br />
25791      * drop: Traditional bottom-right drop shadow (default)
25792      */
25793     /**
25794      * @cfg {String} offset
25795      * The number of pixels to offset the shadow from the element (defaults to 4)
25796      */
25797     offset: 4,
25798
25799     // private
25800     defaultMode: "drop",
25801
25802     /**
25803      * Displays the shadow under the target element
25804      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25805      */
25806     show : function(target){
25807         target = Roo.get(target);
25808         if(!this.el){
25809             this.el = Roo.Shadow.Pool.pull();
25810             if(this.el.dom.nextSibling != target.dom){
25811                 this.el.insertBefore(target);
25812             }
25813         }
25814         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25815         if(Roo.isIE){
25816             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25817         }
25818         this.realign(
25819             target.getLeft(true),
25820             target.getTop(true),
25821             target.getWidth(),
25822             target.getHeight()
25823         );
25824         this.el.dom.style.display = "block";
25825     },
25826
25827     /**
25828      * Returns true if the shadow is visible, else false
25829      */
25830     isVisible : function(){
25831         return this.el ? true : false;  
25832     },
25833
25834     /**
25835      * Direct alignment when values are already available. Show must be called at least once before
25836      * calling this method to ensure it is initialized.
25837      * @param {Number} left The target element left position
25838      * @param {Number} top The target element top position
25839      * @param {Number} width The target element width
25840      * @param {Number} height The target element height
25841      */
25842     realign : function(l, t, w, h){
25843         if(!this.el){
25844             return;
25845         }
25846         var a = this.adjusts, d = this.el.dom, s = d.style;
25847         var iea = 0;
25848         s.left = (l+a.l)+"px";
25849         s.top = (t+a.t)+"px";
25850         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25851  
25852         if(s.width != sws || s.height != shs){
25853             s.width = sws;
25854             s.height = shs;
25855             if(!Roo.isIE){
25856                 var cn = d.childNodes;
25857                 var sww = Math.max(0, (sw-12))+"px";
25858                 cn[0].childNodes[1].style.width = sww;
25859                 cn[1].childNodes[1].style.width = sww;
25860                 cn[2].childNodes[1].style.width = sww;
25861                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25862             }
25863         }
25864     },
25865
25866     /**
25867      * Hides this shadow
25868      */
25869     hide : function(){
25870         if(this.el){
25871             this.el.dom.style.display = "none";
25872             Roo.Shadow.Pool.push(this.el);
25873             delete this.el;
25874         }
25875     },
25876
25877     /**
25878      * Adjust the z-index of this shadow
25879      * @param {Number} zindex The new z-index
25880      */
25881     setZIndex : function(z){
25882         this.zIndex = z;
25883         if(this.el){
25884             this.el.setStyle("z-index", z);
25885         }
25886     }
25887 };
25888
25889 // Private utility class that manages the internal Shadow cache
25890 Roo.Shadow.Pool = function(){
25891     var p = [];
25892     var markup = Roo.isIE ?
25893                  '<div class="x-ie-shadow"></div>' :
25894                  '<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>';
25895     return {
25896         pull : function(){
25897             var sh = p.shift();
25898             if(!sh){
25899                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25900                 sh.autoBoxAdjust = false;
25901             }
25902             return sh;
25903         },
25904
25905         push : function(sh){
25906             p.push(sh);
25907         }
25908     };
25909 }();/*
25910  * Based on:
25911  * Ext JS Library 1.1.1
25912  * Copyright(c) 2006-2007, Ext JS, LLC.
25913  *
25914  * Originally Released Under LGPL - original licence link has changed is not relivant.
25915  *
25916  * Fork - LGPL
25917  * <script type="text/javascript">
25918  */
25919
25920
25921 /**
25922  * @class Roo.SplitBar
25923  * @extends Roo.util.Observable
25924  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25925  * <br><br>
25926  * Usage:
25927  * <pre><code>
25928 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25929                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25930 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25931 split.minSize = 100;
25932 split.maxSize = 600;
25933 split.animate = true;
25934 split.on('moved', splitterMoved);
25935 </code></pre>
25936  * @constructor
25937  * Create a new SplitBar
25938  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25939  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25940  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25941  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25942                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25943                         position of the SplitBar).
25944  */
25945 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25946     
25947     /** @private */
25948     this.el = Roo.get(dragElement, true);
25949     this.el.dom.unselectable = "on";
25950     /** @private */
25951     this.resizingEl = Roo.get(resizingElement, true);
25952
25953     /**
25954      * @private
25955      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25956      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25957      * @type Number
25958      */
25959     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25960     
25961     /**
25962      * The minimum size of the resizing element. (Defaults to 0)
25963      * @type Number
25964      */
25965     this.minSize = 0;
25966     
25967     /**
25968      * The maximum size of the resizing element. (Defaults to 2000)
25969      * @type Number
25970      */
25971     this.maxSize = 2000;
25972     
25973     /**
25974      * Whether to animate the transition to the new size
25975      * @type Boolean
25976      */
25977     this.animate = false;
25978     
25979     /**
25980      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25981      * @type Boolean
25982      */
25983     this.useShim = false;
25984     
25985     /** @private */
25986     this.shim = null;
25987     
25988     if(!existingProxy){
25989         /** @private */
25990         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25991     }else{
25992         this.proxy = Roo.get(existingProxy).dom;
25993     }
25994     /** @private */
25995     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25996     
25997     /** @private */
25998     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25999     
26000     /** @private */
26001     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26002     
26003     /** @private */
26004     this.dragSpecs = {};
26005     
26006     /**
26007      * @private The adapter to use to positon and resize elements
26008      */
26009     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26010     this.adapter.init(this);
26011     
26012     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26013         /** @private */
26014         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26015         this.el.addClass("x-splitbar-h");
26016     }else{
26017         /** @private */
26018         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26019         this.el.addClass("x-splitbar-v");
26020     }
26021     
26022     this.addEvents({
26023         /**
26024          * @event resize
26025          * Fires when the splitter is moved (alias for {@link #event-moved})
26026          * @param {Roo.SplitBar} this
26027          * @param {Number} newSize the new width or height
26028          */
26029         "resize" : true,
26030         /**
26031          * @event moved
26032          * Fires when the splitter is moved
26033          * @param {Roo.SplitBar} this
26034          * @param {Number} newSize the new width or height
26035          */
26036         "moved" : true,
26037         /**
26038          * @event beforeresize
26039          * Fires before the splitter is dragged
26040          * @param {Roo.SplitBar} this
26041          */
26042         "beforeresize" : true,
26043
26044         "beforeapply" : true
26045     });
26046
26047     Roo.util.Observable.call(this);
26048 };
26049
26050 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26051     onStartProxyDrag : function(x, y){
26052         this.fireEvent("beforeresize", this);
26053         if(!this.overlay){
26054             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26055             o.unselectable();
26056             o.enableDisplayMode("block");
26057             // all splitbars share the same overlay
26058             Roo.SplitBar.prototype.overlay = o;
26059         }
26060         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26061         this.overlay.show();
26062         Roo.get(this.proxy).setDisplayed("block");
26063         var size = this.adapter.getElementSize(this);
26064         this.activeMinSize = this.getMinimumSize();;
26065         this.activeMaxSize = this.getMaximumSize();;
26066         var c1 = size - this.activeMinSize;
26067         var c2 = Math.max(this.activeMaxSize - size, 0);
26068         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26069             this.dd.resetConstraints();
26070             this.dd.setXConstraint(
26071                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26072                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26073             );
26074             this.dd.setYConstraint(0, 0);
26075         }else{
26076             this.dd.resetConstraints();
26077             this.dd.setXConstraint(0, 0);
26078             this.dd.setYConstraint(
26079                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26080                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26081             );
26082          }
26083         this.dragSpecs.startSize = size;
26084         this.dragSpecs.startPoint = [x, y];
26085         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26086     },
26087     
26088     /** 
26089      * @private Called after the drag operation by the DDProxy
26090      */
26091     onEndProxyDrag : function(e){
26092         Roo.get(this.proxy).setDisplayed(false);
26093         var endPoint = Roo.lib.Event.getXY(e);
26094         if(this.overlay){
26095             this.overlay.hide();
26096         }
26097         var newSize;
26098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26099             newSize = this.dragSpecs.startSize + 
26100                 (this.placement == Roo.SplitBar.LEFT ?
26101                     endPoint[0] - this.dragSpecs.startPoint[0] :
26102                     this.dragSpecs.startPoint[0] - endPoint[0]
26103                 );
26104         }else{
26105             newSize = this.dragSpecs.startSize + 
26106                 (this.placement == Roo.SplitBar.TOP ?
26107                     endPoint[1] - this.dragSpecs.startPoint[1] :
26108                     this.dragSpecs.startPoint[1] - endPoint[1]
26109                 );
26110         }
26111         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26112         if(newSize != this.dragSpecs.startSize){
26113             if(this.fireEvent('beforeapply', this, newSize) !== false){
26114                 this.adapter.setElementSize(this, newSize);
26115                 this.fireEvent("moved", this, newSize);
26116                 this.fireEvent("resize", this, newSize);
26117             }
26118         }
26119     },
26120     
26121     /**
26122      * Get the adapter this SplitBar uses
26123      * @return The adapter object
26124      */
26125     getAdapter : function(){
26126         return this.adapter;
26127     },
26128     
26129     /**
26130      * Set the adapter this SplitBar uses
26131      * @param {Object} adapter A SplitBar adapter object
26132      */
26133     setAdapter : function(adapter){
26134         this.adapter = adapter;
26135         this.adapter.init(this);
26136     },
26137     
26138     /**
26139      * Gets the minimum size for the resizing element
26140      * @return {Number} The minimum size
26141      */
26142     getMinimumSize : function(){
26143         return this.minSize;
26144     },
26145     
26146     /**
26147      * Sets the minimum size for the resizing element
26148      * @param {Number} minSize The minimum size
26149      */
26150     setMinimumSize : function(minSize){
26151         this.minSize = minSize;
26152     },
26153     
26154     /**
26155      * Gets the maximum size for the resizing element
26156      * @return {Number} The maximum size
26157      */
26158     getMaximumSize : function(){
26159         return this.maxSize;
26160     },
26161     
26162     /**
26163      * Sets the maximum size for the resizing element
26164      * @param {Number} maxSize The maximum size
26165      */
26166     setMaximumSize : function(maxSize){
26167         this.maxSize = maxSize;
26168     },
26169     
26170     /**
26171      * Sets the initialize size for the resizing element
26172      * @param {Number} size The initial size
26173      */
26174     setCurrentSize : function(size){
26175         var oldAnimate = this.animate;
26176         this.animate = false;
26177         this.adapter.setElementSize(this, size);
26178         this.animate = oldAnimate;
26179     },
26180     
26181     /**
26182      * Destroy this splitbar. 
26183      * @param {Boolean} removeEl True to remove the element
26184      */
26185     destroy : function(removeEl){
26186         if(this.shim){
26187             this.shim.remove();
26188         }
26189         this.dd.unreg();
26190         this.proxy.parentNode.removeChild(this.proxy);
26191         if(removeEl){
26192             this.el.remove();
26193         }
26194     }
26195 });
26196
26197 /**
26198  * @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.
26199  */
26200 Roo.SplitBar.createProxy = function(dir){
26201     var proxy = new Roo.Element(document.createElement("div"));
26202     proxy.unselectable();
26203     var cls = 'x-splitbar-proxy';
26204     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26205     document.body.appendChild(proxy.dom);
26206     return proxy.dom;
26207 };
26208
26209 /** 
26210  * @class Roo.SplitBar.BasicLayoutAdapter
26211  * Default Adapter. It assumes the splitter and resizing element are not positioned
26212  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26213  */
26214 Roo.SplitBar.BasicLayoutAdapter = function(){
26215 };
26216
26217 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26218     // do nothing for now
26219     init : function(s){
26220     
26221     },
26222     /**
26223      * Called before drag operations to get the current size of the resizing element. 
26224      * @param {Roo.SplitBar} s The SplitBar using this adapter
26225      */
26226      getElementSize : function(s){
26227         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26228             return s.resizingEl.getWidth();
26229         }else{
26230             return s.resizingEl.getHeight();
26231         }
26232     },
26233     
26234     /**
26235      * Called after drag operations to set the size of the resizing element.
26236      * @param {Roo.SplitBar} s The SplitBar using this adapter
26237      * @param {Number} newSize The new size to set
26238      * @param {Function} onComplete A function to be invoked when resizing is complete
26239      */
26240     setElementSize : function(s, newSize, onComplete){
26241         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26242             if(!s.animate){
26243                 s.resizingEl.setWidth(newSize);
26244                 if(onComplete){
26245                     onComplete(s, newSize);
26246                 }
26247             }else{
26248                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26249             }
26250         }else{
26251             
26252             if(!s.animate){
26253                 s.resizingEl.setHeight(newSize);
26254                 if(onComplete){
26255                     onComplete(s, newSize);
26256                 }
26257             }else{
26258                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26259             }
26260         }
26261     }
26262 };
26263
26264 /** 
26265  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26266  * @extends Roo.SplitBar.BasicLayoutAdapter
26267  * Adapter that  moves the splitter element to align with the resized sizing element. 
26268  * Used with an absolute positioned SplitBar.
26269  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26270  * document.body, make sure you assign an id to the body element.
26271  */
26272 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26273     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26274     this.container = Roo.get(container);
26275 };
26276
26277 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26278     init : function(s){
26279         this.basic.init(s);
26280     },
26281     
26282     getElementSize : function(s){
26283         return this.basic.getElementSize(s);
26284     },
26285     
26286     setElementSize : function(s, newSize, onComplete){
26287         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26288     },
26289     
26290     moveSplitter : function(s){
26291         var yes = Roo.SplitBar;
26292         switch(s.placement){
26293             case yes.LEFT:
26294                 s.el.setX(s.resizingEl.getRight());
26295                 break;
26296             case yes.RIGHT:
26297                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26298                 break;
26299             case yes.TOP:
26300                 s.el.setY(s.resizingEl.getBottom());
26301                 break;
26302             case yes.BOTTOM:
26303                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26304                 break;
26305         }
26306     }
26307 };
26308
26309 /**
26310  * Orientation constant - Create a vertical SplitBar
26311  * @static
26312  * @type Number
26313  */
26314 Roo.SplitBar.VERTICAL = 1;
26315
26316 /**
26317  * Orientation constant - Create a horizontal SplitBar
26318  * @static
26319  * @type Number
26320  */
26321 Roo.SplitBar.HORIZONTAL = 2;
26322
26323 /**
26324  * Placement constant - The resizing element is to the left of the splitter element
26325  * @static
26326  * @type Number
26327  */
26328 Roo.SplitBar.LEFT = 1;
26329
26330 /**
26331  * Placement constant - The resizing element is to the right of the splitter element
26332  * @static
26333  * @type Number
26334  */
26335 Roo.SplitBar.RIGHT = 2;
26336
26337 /**
26338  * Placement constant - The resizing element is positioned above the splitter element
26339  * @static
26340  * @type Number
26341  */
26342 Roo.SplitBar.TOP = 3;
26343
26344 /**
26345  * Placement constant - The resizing element is positioned under splitter element
26346  * @static
26347  * @type Number
26348  */
26349 Roo.SplitBar.BOTTOM = 4;
26350 /*
26351  * Based on:
26352  * Ext JS Library 1.1.1
26353  * Copyright(c) 2006-2007, Ext JS, LLC.
26354  *
26355  * Originally Released Under LGPL - original licence link has changed is not relivant.
26356  *
26357  * Fork - LGPL
26358  * <script type="text/javascript">
26359  */
26360
26361 /**
26362  * @class Roo.View
26363  * @extends Roo.util.Observable
26364  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26365  * This class also supports single and multi selection modes. <br>
26366  * Create a data model bound view:
26367  <pre><code>
26368  var store = new Roo.data.Store(...);
26369
26370  var view = new Roo.View({
26371     el : "my-element",
26372     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26373  
26374     singleSelect: true,
26375     selectedClass: "ydataview-selected",
26376     store: store
26377  });
26378
26379  // listen for node click?
26380  view.on("click", function(vw, index, node, e){
26381  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26382  });
26383
26384  // load XML data
26385  dataModel.load("foobar.xml");
26386  </code></pre>
26387  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26388  * <br><br>
26389  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26390  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26391  * 
26392  * Note: old style constructor is still suported (container, template, config)
26393  * 
26394  * @constructor
26395  * Create a new View
26396  * @param {Object} config The config object
26397  * 
26398  */
26399 Roo.View = function(config, depreciated_tpl, depreciated_config){
26400     
26401     this.parent = false;
26402     
26403     if (typeof(depreciated_tpl) == 'undefined') {
26404         // new way.. - universal constructor.
26405         Roo.apply(this, config);
26406         this.el  = Roo.get(this.el);
26407     } else {
26408         // old format..
26409         this.el  = Roo.get(config);
26410         this.tpl = depreciated_tpl;
26411         Roo.apply(this, depreciated_config);
26412     }
26413     this.wrapEl  = this.el.wrap().wrap();
26414     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26415     
26416     
26417     if(typeof(this.tpl) == "string"){
26418         this.tpl = new Roo.Template(this.tpl);
26419     } else {
26420         // support xtype ctors..
26421         this.tpl = new Roo.factory(this.tpl, Roo);
26422     }
26423     
26424     
26425     this.tpl.compile();
26426     
26427     /** @private */
26428     this.addEvents({
26429         /**
26430          * @event beforeclick
26431          * Fires before a click is processed. Returns false to cancel the default action.
26432          * @param {Roo.View} this
26433          * @param {Number} index The index of the target node
26434          * @param {HTMLElement} node The target node
26435          * @param {Roo.EventObject} e The raw event object
26436          */
26437             "beforeclick" : true,
26438         /**
26439          * @event click
26440          * Fires when a template node is clicked.
26441          * @param {Roo.View} this
26442          * @param {Number} index The index of the target node
26443          * @param {HTMLElement} node The target node
26444          * @param {Roo.EventObject} e The raw event object
26445          */
26446             "click" : true,
26447         /**
26448          * @event dblclick
26449          * Fires when a template node is double clicked.
26450          * @param {Roo.View} this
26451          * @param {Number} index The index of the target node
26452          * @param {HTMLElement} node The target node
26453          * @param {Roo.EventObject} e The raw event object
26454          */
26455             "dblclick" : true,
26456         /**
26457          * @event contextmenu
26458          * Fires when a template node is right clicked.
26459          * @param {Roo.View} this
26460          * @param {Number} index The index of the target node
26461          * @param {HTMLElement} node The target node
26462          * @param {Roo.EventObject} e The raw event object
26463          */
26464             "contextmenu" : true,
26465         /**
26466          * @event selectionchange
26467          * Fires when the selected nodes change.
26468          * @param {Roo.View} this
26469          * @param {Array} selections Array of the selected nodes
26470          */
26471             "selectionchange" : true,
26472     
26473         /**
26474          * @event beforeselect
26475          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26476          * @param {Roo.View} this
26477          * @param {HTMLElement} node The node to be selected
26478          * @param {Array} selections Array of currently selected nodes
26479          */
26480             "beforeselect" : true,
26481         /**
26482          * @event preparedata
26483          * Fires on every row to render, to allow you to change the data.
26484          * @param {Roo.View} this
26485          * @param {Object} data to be rendered (change this)
26486          */
26487           "preparedata" : true
26488           
26489           
26490         });
26491
26492
26493
26494     this.el.on({
26495         "click": this.onClick,
26496         "dblclick": this.onDblClick,
26497         "contextmenu": this.onContextMenu,
26498         scope:this
26499     });
26500
26501     this.selections = [];
26502     this.nodes = [];
26503     this.cmp = new Roo.CompositeElementLite([]);
26504     if(this.store){
26505         this.store = Roo.factory(this.store, Roo.data);
26506         this.setStore(this.store, true);
26507     }
26508     
26509     if ( this.footer && this.footer.xtype) {
26510            
26511          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26512         
26513         this.footer.dataSource = this.store;
26514         this.footer.container = fctr;
26515         this.footer = Roo.factory(this.footer, Roo);
26516         fctr.insertFirst(this.el);
26517         
26518         // this is a bit insane - as the paging toolbar seems to detach the el..
26519 //        dom.parentNode.parentNode.parentNode
26520          // they get detached?
26521     }
26522     
26523     
26524     Roo.View.superclass.constructor.call(this);
26525     
26526     
26527 };
26528
26529 Roo.extend(Roo.View, Roo.util.Observable, {
26530     
26531      /**
26532      * @cfg {Roo.data.Store} store Data store to load data from.
26533      */
26534     store : false,
26535     
26536     /**
26537      * @cfg {String|Roo.Element} el The container element.
26538      */
26539     el : '',
26540     
26541     /**
26542      * @cfg {String|Roo.Template} tpl The template used by this View 
26543      */
26544     tpl : false,
26545     /**
26546      * @cfg {String} dataName the named area of the template to use as the data area
26547      *                          Works with domtemplates roo-name="name"
26548      */
26549     dataName: false,
26550     /**
26551      * @cfg {String} selectedClass The css class to add to selected nodes
26552      */
26553     selectedClass : "x-view-selected",
26554      /**
26555      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26556      */
26557     emptyText : "",
26558     
26559     /**
26560      * @cfg {String} text to display on mask (default Loading)
26561      */
26562     mask : false,
26563     /**
26564      * @cfg {Boolean} multiSelect Allow multiple selection
26565      */
26566     multiSelect : false,
26567     /**
26568      * @cfg {Boolean} singleSelect Allow single selection
26569      */
26570     singleSelect:  false,
26571     
26572     /**
26573      * @cfg {Boolean} toggleSelect - selecting 
26574      */
26575     toggleSelect : false,
26576     
26577     /**
26578      * @cfg {Boolean} tickable - selecting 
26579      */
26580     tickable : false,
26581     
26582     /**
26583      * Returns the element this view is bound to.
26584      * @return {Roo.Element}
26585      */
26586     getEl : function(){
26587         return this.wrapEl;
26588     },
26589     
26590     
26591
26592     /**
26593      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26594      */
26595     refresh : function(){
26596         //Roo.log('refresh');
26597         var t = this.tpl;
26598         
26599         // if we are using something like 'domtemplate', then
26600         // the what gets used is:
26601         // t.applySubtemplate(NAME, data, wrapping data..)
26602         // the outer template then get' applied with
26603         //     the store 'extra data'
26604         // and the body get's added to the
26605         //      roo-name="data" node?
26606         //      <span class='roo-tpl-{name}'></span> ?????
26607         
26608         
26609         
26610         this.clearSelections();
26611         this.el.update("");
26612         var html = [];
26613         var records = this.store.getRange();
26614         if(records.length < 1) {
26615             
26616             // is this valid??  = should it render a template??
26617             
26618             this.el.update(this.emptyText);
26619             return;
26620         }
26621         var el = this.el;
26622         if (this.dataName) {
26623             this.el.update(t.apply(this.store.meta)); //????
26624             el = this.el.child('.roo-tpl-' + this.dataName);
26625         }
26626         
26627         for(var i = 0, len = records.length; i < len; i++){
26628             var data = this.prepareData(records[i].data, i, records[i]);
26629             this.fireEvent("preparedata", this, data, i, records[i]);
26630             
26631             var d = Roo.apply({}, data);
26632             
26633             if(this.tickable){
26634                 Roo.apply(d, {'roo-id' : Roo.id()});
26635                 
26636                 var _this = this;
26637             
26638                 Roo.each(this.parent.item, function(item){
26639                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26640                         return;
26641                     }
26642                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26643                 });
26644             }
26645             
26646             html[html.length] = Roo.util.Format.trim(
26647                 this.dataName ?
26648                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26649                     t.apply(d)
26650             );
26651         }
26652         
26653         
26654         
26655         el.update(html.join(""));
26656         this.nodes = el.dom.childNodes;
26657         this.updateIndexes(0);
26658     },
26659     
26660
26661     /**
26662      * Function to override to reformat the data that is sent to
26663      * the template for each node.
26664      * DEPRICATED - use the preparedata event handler.
26665      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26666      * a JSON object for an UpdateManager bound view).
26667      */
26668     prepareData : function(data, index, record)
26669     {
26670         this.fireEvent("preparedata", this, data, index, record);
26671         return data;
26672     },
26673
26674     onUpdate : function(ds, record){
26675         // Roo.log('on update');   
26676         this.clearSelections();
26677         var index = this.store.indexOf(record);
26678         var n = this.nodes[index];
26679         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26680         n.parentNode.removeChild(n);
26681         this.updateIndexes(index, index);
26682     },
26683
26684     
26685     
26686 // --------- FIXME     
26687     onAdd : function(ds, records, index)
26688     {
26689         //Roo.log(['on Add', ds, records, index] );        
26690         this.clearSelections();
26691         if(this.nodes.length == 0){
26692             this.refresh();
26693             return;
26694         }
26695         var n = this.nodes[index];
26696         for(var i = 0, len = records.length; i < len; i++){
26697             var d = this.prepareData(records[i].data, i, records[i]);
26698             if(n){
26699                 this.tpl.insertBefore(n, d);
26700             }else{
26701                 
26702                 this.tpl.append(this.el, d);
26703             }
26704         }
26705         this.updateIndexes(index);
26706     },
26707
26708     onRemove : function(ds, record, index){
26709        // Roo.log('onRemove');
26710         this.clearSelections();
26711         var el = this.dataName  ?
26712             this.el.child('.roo-tpl-' + this.dataName) :
26713             this.el; 
26714         
26715         el.dom.removeChild(this.nodes[index]);
26716         this.updateIndexes(index);
26717     },
26718
26719     /**
26720      * Refresh an individual node.
26721      * @param {Number} index
26722      */
26723     refreshNode : function(index){
26724         this.onUpdate(this.store, this.store.getAt(index));
26725     },
26726
26727     updateIndexes : function(startIndex, endIndex){
26728         var ns = this.nodes;
26729         startIndex = startIndex || 0;
26730         endIndex = endIndex || ns.length - 1;
26731         for(var i = startIndex; i <= endIndex; i++){
26732             ns[i].nodeIndex = i;
26733         }
26734     },
26735
26736     /**
26737      * Changes the data store this view uses and refresh the view.
26738      * @param {Store} store
26739      */
26740     setStore : function(store, initial){
26741         if(!initial && this.store){
26742             this.store.un("datachanged", this.refresh);
26743             this.store.un("add", this.onAdd);
26744             this.store.un("remove", this.onRemove);
26745             this.store.un("update", this.onUpdate);
26746             this.store.un("clear", this.refresh);
26747             this.store.un("beforeload", this.onBeforeLoad);
26748             this.store.un("load", this.onLoad);
26749             this.store.un("loadexception", this.onLoad);
26750         }
26751         if(store){
26752           
26753             store.on("datachanged", this.refresh, this);
26754             store.on("add", this.onAdd, this);
26755             store.on("remove", this.onRemove, this);
26756             store.on("update", this.onUpdate, this);
26757             store.on("clear", this.refresh, this);
26758             store.on("beforeload", this.onBeforeLoad, this);
26759             store.on("load", this.onLoad, this);
26760             store.on("loadexception", this.onLoad, this);
26761         }
26762         
26763         if(store){
26764             this.refresh();
26765         }
26766     },
26767     /**
26768      * onbeforeLoad - masks the loading area.
26769      *
26770      */
26771     onBeforeLoad : function(store,opts)
26772     {
26773          //Roo.log('onBeforeLoad');   
26774         if (!opts.add) {
26775             this.el.update("");
26776         }
26777         this.el.mask(this.mask ? this.mask : "Loading" ); 
26778     },
26779     onLoad : function ()
26780     {
26781         this.el.unmask();
26782     },
26783     
26784
26785     /**
26786      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26787      * @param {HTMLElement} node
26788      * @return {HTMLElement} The template node
26789      */
26790     findItemFromChild : function(node){
26791         var el = this.dataName  ?
26792             this.el.child('.roo-tpl-' + this.dataName,true) :
26793             this.el.dom; 
26794         
26795         if(!node || node.parentNode == el){
26796                     return node;
26797             }
26798             var p = node.parentNode;
26799             while(p && p != el){
26800             if(p.parentNode == el){
26801                 return p;
26802             }
26803             p = p.parentNode;
26804         }
26805             return null;
26806     },
26807
26808     /** @ignore */
26809     onClick : function(e){
26810         var item = this.findItemFromChild(e.getTarget());
26811         if(item){
26812             var index = this.indexOf(item);
26813             if(this.onItemClick(item, index, e) !== false){
26814                 this.fireEvent("click", this, index, item, e);
26815             }
26816         }else{
26817             this.clearSelections();
26818         }
26819     },
26820
26821     /** @ignore */
26822     onContextMenu : function(e){
26823         var item = this.findItemFromChild(e.getTarget());
26824         if(item){
26825             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26826         }
26827     },
26828
26829     /** @ignore */
26830     onDblClick : function(e){
26831         var item = this.findItemFromChild(e.getTarget());
26832         if(item){
26833             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26834         }
26835     },
26836
26837     onItemClick : function(item, index, e)
26838     {
26839         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26840             return false;
26841         }
26842         if (this.toggleSelect) {
26843             var m = this.isSelected(item) ? 'unselect' : 'select';
26844             //Roo.log(m);
26845             var _t = this;
26846             _t[m](item, true, false);
26847             return true;
26848         }
26849         if(this.multiSelect || this.singleSelect){
26850             if(this.multiSelect && e.shiftKey && this.lastSelection){
26851                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26852             }else{
26853                 this.select(item, this.multiSelect && e.ctrlKey);
26854                 this.lastSelection = item;
26855             }
26856             
26857             if(!this.tickable){
26858                 e.preventDefault();
26859             }
26860             
26861         }
26862         return true;
26863     },
26864
26865     /**
26866      * Get the number of selected nodes.
26867      * @return {Number}
26868      */
26869     getSelectionCount : function(){
26870         return this.selections.length;
26871     },
26872
26873     /**
26874      * Get the currently selected nodes.
26875      * @return {Array} An array of HTMLElements
26876      */
26877     getSelectedNodes : function(){
26878         return this.selections;
26879     },
26880
26881     /**
26882      * Get the indexes of the selected nodes.
26883      * @return {Array}
26884      */
26885     getSelectedIndexes : function(){
26886         var indexes = [], s = this.selections;
26887         for(var i = 0, len = s.length; i < len; i++){
26888             indexes.push(s[i].nodeIndex);
26889         }
26890         return indexes;
26891     },
26892
26893     /**
26894      * Clear all selections
26895      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26896      */
26897     clearSelections : function(suppressEvent){
26898         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26899             this.cmp.elements = this.selections;
26900             this.cmp.removeClass(this.selectedClass);
26901             this.selections = [];
26902             if(!suppressEvent){
26903                 this.fireEvent("selectionchange", this, this.selections);
26904             }
26905         }
26906     },
26907
26908     /**
26909      * Returns true if the passed node is selected
26910      * @param {HTMLElement/Number} node The node or node index
26911      * @return {Boolean}
26912      */
26913     isSelected : function(node){
26914         var s = this.selections;
26915         if(s.length < 1){
26916             return false;
26917         }
26918         node = this.getNode(node);
26919         return s.indexOf(node) !== -1;
26920     },
26921
26922     /**
26923      * Selects nodes.
26924      * @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
26925      * @param {Boolean} keepExisting (optional) true to keep existing selections
26926      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26927      */
26928     select : function(nodeInfo, keepExisting, suppressEvent){
26929         if(nodeInfo instanceof Array){
26930             if(!keepExisting){
26931                 this.clearSelections(true);
26932             }
26933             for(var i = 0, len = nodeInfo.length; i < len; i++){
26934                 this.select(nodeInfo[i], true, true);
26935             }
26936             return;
26937         } 
26938         var node = this.getNode(nodeInfo);
26939         if(!node || this.isSelected(node)){
26940             return; // already selected.
26941         }
26942         if(!keepExisting){
26943             this.clearSelections(true);
26944         }
26945         
26946         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26947             Roo.fly(node).addClass(this.selectedClass);
26948             this.selections.push(node);
26949             if(!suppressEvent){
26950                 this.fireEvent("selectionchange", this, this.selections);
26951             }
26952         }
26953         
26954         
26955     },
26956       /**
26957      * Unselects nodes.
26958      * @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
26959      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26960      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26961      */
26962     unselect : function(nodeInfo, keepExisting, suppressEvent)
26963     {
26964         if(nodeInfo instanceof Array){
26965             Roo.each(this.selections, function(s) {
26966                 this.unselect(s, nodeInfo);
26967             }, this);
26968             return;
26969         }
26970         var node = this.getNode(nodeInfo);
26971         if(!node || !this.isSelected(node)){
26972             //Roo.log("not selected");
26973             return; // not selected.
26974         }
26975         // fireevent???
26976         var ns = [];
26977         Roo.each(this.selections, function(s) {
26978             if (s == node ) {
26979                 Roo.fly(node).removeClass(this.selectedClass);
26980
26981                 return;
26982             }
26983             ns.push(s);
26984         },this);
26985         
26986         this.selections= ns;
26987         this.fireEvent("selectionchange", this, this.selections);
26988     },
26989
26990     /**
26991      * Gets a template node.
26992      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26993      * @return {HTMLElement} The node or null if it wasn't found
26994      */
26995     getNode : function(nodeInfo){
26996         if(typeof nodeInfo == "string"){
26997             return document.getElementById(nodeInfo);
26998         }else if(typeof nodeInfo == "number"){
26999             return this.nodes[nodeInfo];
27000         }
27001         return nodeInfo;
27002     },
27003
27004     /**
27005      * Gets a range template nodes.
27006      * @param {Number} startIndex
27007      * @param {Number} endIndex
27008      * @return {Array} An array of nodes
27009      */
27010     getNodes : function(start, end){
27011         var ns = this.nodes;
27012         start = start || 0;
27013         end = typeof end == "undefined" ? ns.length - 1 : end;
27014         var nodes = [];
27015         if(start <= end){
27016             for(var i = start; i <= end; i++){
27017                 nodes.push(ns[i]);
27018             }
27019         } else{
27020             for(var i = start; i >= end; i--){
27021                 nodes.push(ns[i]);
27022             }
27023         }
27024         return nodes;
27025     },
27026
27027     /**
27028      * Finds the index of the passed node
27029      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27030      * @return {Number} The index of the node or -1
27031      */
27032     indexOf : function(node){
27033         node = this.getNode(node);
27034         if(typeof node.nodeIndex == "number"){
27035             return node.nodeIndex;
27036         }
27037         var ns = this.nodes;
27038         for(var i = 0, len = ns.length; i < len; i++){
27039             if(ns[i] == node){
27040                 return i;
27041             }
27042         }
27043         return -1;
27044     }
27045 });
27046 /*
27047  * Based on:
27048  * Ext JS Library 1.1.1
27049  * Copyright(c) 2006-2007, Ext JS, LLC.
27050  *
27051  * Originally Released Under LGPL - original licence link has changed is not relivant.
27052  *
27053  * Fork - LGPL
27054  * <script type="text/javascript">
27055  */
27056
27057 /**
27058  * @class Roo.JsonView
27059  * @extends Roo.View
27060  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27061 <pre><code>
27062 var view = new Roo.JsonView({
27063     container: "my-element",
27064     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27065     multiSelect: true, 
27066     jsonRoot: "data" 
27067 });
27068
27069 // listen for node click?
27070 view.on("click", function(vw, index, node, e){
27071     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27072 });
27073
27074 // direct load of JSON data
27075 view.load("foobar.php");
27076
27077 // Example from my blog list
27078 var tpl = new Roo.Template(
27079     '&lt;div class="entry"&gt;' +
27080     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27081     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27082     "&lt;/div&gt;&lt;hr /&gt;"
27083 );
27084
27085 var moreView = new Roo.JsonView({
27086     container :  "entry-list", 
27087     template : tpl,
27088     jsonRoot: "posts"
27089 });
27090 moreView.on("beforerender", this.sortEntries, this);
27091 moreView.load({
27092     url: "/blog/get-posts.php",
27093     params: "allposts=true",
27094     text: "Loading Blog Entries..."
27095 });
27096 </code></pre>
27097
27098 * Note: old code is supported with arguments : (container, template, config)
27099
27100
27101  * @constructor
27102  * Create a new JsonView
27103  * 
27104  * @param {Object} config The config object
27105  * 
27106  */
27107 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27108     
27109     
27110     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27111
27112     var um = this.el.getUpdateManager();
27113     um.setRenderer(this);
27114     um.on("update", this.onLoad, this);
27115     um.on("failure", this.onLoadException, this);
27116
27117     /**
27118      * @event beforerender
27119      * Fires before rendering of the downloaded JSON data.
27120      * @param {Roo.JsonView} this
27121      * @param {Object} data The JSON data loaded
27122      */
27123     /**
27124      * @event load
27125      * Fires when data is loaded.
27126      * @param {Roo.JsonView} this
27127      * @param {Object} data The JSON data loaded
27128      * @param {Object} response The raw Connect response object
27129      */
27130     /**
27131      * @event loadexception
27132      * Fires when loading fails.
27133      * @param {Roo.JsonView} this
27134      * @param {Object} response The raw Connect response object
27135      */
27136     this.addEvents({
27137         'beforerender' : true,
27138         'load' : true,
27139         'loadexception' : true
27140     });
27141 };
27142 Roo.extend(Roo.JsonView, Roo.View, {
27143     /**
27144      * @type {String} The root property in the loaded JSON object that contains the data
27145      */
27146     jsonRoot : "",
27147
27148     /**
27149      * Refreshes the view.
27150      */
27151     refresh : function(){
27152         this.clearSelections();
27153         this.el.update("");
27154         var html = [];
27155         var o = this.jsonData;
27156         if(o && o.length > 0){
27157             for(var i = 0, len = o.length; i < len; i++){
27158                 var data = this.prepareData(o[i], i, o);
27159                 html[html.length] = this.tpl.apply(data);
27160             }
27161         }else{
27162             html.push(this.emptyText);
27163         }
27164         this.el.update(html.join(""));
27165         this.nodes = this.el.dom.childNodes;
27166         this.updateIndexes(0);
27167     },
27168
27169     /**
27170      * 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.
27171      * @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:
27172      <pre><code>
27173      view.load({
27174          url: "your-url.php",
27175          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27176          callback: yourFunction,
27177          scope: yourObject, //(optional scope)
27178          discardUrl: false,
27179          nocache: false,
27180          text: "Loading...",
27181          timeout: 30,
27182          scripts: false
27183      });
27184      </code></pre>
27185      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27186      * 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.
27187      * @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}
27188      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27189      * @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.
27190      */
27191     load : function(){
27192         var um = this.el.getUpdateManager();
27193         um.update.apply(um, arguments);
27194     },
27195
27196     // note - render is a standard framework call...
27197     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27198     render : function(el, response){
27199         
27200         this.clearSelections();
27201         this.el.update("");
27202         var o;
27203         try{
27204             if (response != '') {
27205                 o = Roo.util.JSON.decode(response.responseText);
27206                 if(this.jsonRoot){
27207                     
27208                     o = o[this.jsonRoot];
27209                 }
27210             }
27211         } catch(e){
27212         }
27213         /**
27214          * The current JSON data or null
27215          */
27216         this.jsonData = o;
27217         this.beforeRender();
27218         this.refresh();
27219     },
27220
27221 /**
27222  * Get the number of records in the current JSON dataset
27223  * @return {Number}
27224  */
27225     getCount : function(){
27226         return this.jsonData ? this.jsonData.length : 0;
27227     },
27228
27229 /**
27230  * Returns the JSON object for the specified node(s)
27231  * @param {HTMLElement/Array} node The node or an array of nodes
27232  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27233  * you get the JSON object for the node
27234  */
27235     getNodeData : function(node){
27236         if(node instanceof Array){
27237             var data = [];
27238             for(var i = 0, len = node.length; i < len; i++){
27239                 data.push(this.getNodeData(node[i]));
27240             }
27241             return data;
27242         }
27243         return this.jsonData[this.indexOf(node)] || null;
27244     },
27245
27246     beforeRender : function(){
27247         this.snapshot = this.jsonData;
27248         if(this.sortInfo){
27249             this.sort.apply(this, this.sortInfo);
27250         }
27251         this.fireEvent("beforerender", this, this.jsonData);
27252     },
27253
27254     onLoad : function(el, o){
27255         this.fireEvent("load", this, this.jsonData, o);
27256     },
27257
27258     onLoadException : function(el, o){
27259         this.fireEvent("loadexception", this, o);
27260     },
27261
27262 /**
27263  * Filter the data by a specific property.
27264  * @param {String} property A property on your JSON objects
27265  * @param {String/RegExp} value Either string that the property values
27266  * should start with, or a RegExp to test against the property
27267  */
27268     filter : function(property, value){
27269         if(this.jsonData){
27270             var data = [];
27271             var ss = this.snapshot;
27272             if(typeof value == "string"){
27273                 var vlen = value.length;
27274                 if(vlen == 0){
27275                     this.clearFilter();
27276                     return;
27277                 }
27278                 value = value.toLowerCase();
27279                 for(var i = 0, len = ss.length; i < len; i++){
27280                     var o = ss[i];
27281                     if(o[property].substr(0, vlen).toLowerCase() == value){
27282                         data.push(o);
27283                     }
27284                 }
27285             } else if(value.exec){ // regex?
27286                 for(var i = 0, len = ss.length; i < len; i++){
27287                     var o = ss[i];
27288                     if(value.test(o[property])){
27289                         data.push(o);
27290                     }
27291                 }
27292             } else{
27293                 return;
27294             }
27295             this.jsonData = data;
27296             this.refresh();
27297         }
27298     },
27299
27300 /**
27301  * Filter by a function. The passed function will be called with each
27302  * object in the current dataset. If the function returns true the value is kept,
27303  * otherwise it is filtered.
27304  * @param {Function} fn
27305  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27306  */
27307     filterBy : function(fn, scope){
27308         if(this.jsonData){
27309             var data = [];
27310             var ss = this.snapshot;
27311             for(var i = 0, len = ss.length; i < len; i++){
27312                 var o = ss[i];
27313                 if(fn.call(scope || this, o)){
27314                     data.push(o);
27315                 }
27316             }
27317             this.jsonData = data;
27318             this.refresh();
27319         }
27320     },
27321
27322 /**
27323  * Clears the current filter.
27324  */
27325     clearFilter : function(){
27326         if(this.snapshot && this.jsonData != this.snapshot){
27327             this.jsonData = this.snapshot;
27328             this.refresh();
27329         }
27330     },
27331
27332
27333 /**
27334  * Sorts the data for this view and refreshes it.
27335  * @param {String} property A property on your JSON objects to sort on
27336  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27337  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27338  */
27339     sort : function(property, dir, sortType){
27340         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27341         if(this.jsonData){
27342             var p = property;
27343             var dsc = dir && dir.toLowerCase() == "desc";
27344             var f = function(o1, o2){
27345                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27346                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27347                 ;
27348                 if(v1 < v2){
27349                     return dsc ? +1 : -1;
27350                 } else if(v1 > v2){
27351                     return dsc ? -1 : +1;
27352                 } else{
27353                     return 0;
27354                 }
27355             };
27356             this.jsonData.sort(f);
27357             this.refresh();
27358             if(this.jsonData != this.snapshot){
27359                 this.snapshot.sort(f);
27360             }
27361         }
27362     }
27363 });/*
27364  * Based on:
27365  * Ext JS Library 1.1.1
27366  * Copyright(c) 2006-2007, Ext JS, LLC.
27367  *
27368  * Originally Released Under LGPL - original licence link has changed is not relivant.
27369  *
27370  * Fork - LGPL
27371  * <script type="text/javascript">
27372  */
27373  
27374
27375 /**
27376  * @class Roo.ColorPalette
27377  * @extends Roo.Component
27378  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27379  * Here's an example of typical usage:
27380  * <pre><code>
27381 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27382 cp.render('my-div');
27383
27384 cp.on('select', function(palette, selColor){
27385     // do something with selColor
27386 });
27387 </code></pre>
27388  * @constructor
27389  * Create a new ColorPalette
27390  * @param {Object} config The config object
27391  */
27392 Roo.ColorPalette = function(config){
27393     Roo.ColorPalette.superclass.constructor.call(this, config);
27394     this.addEvents({
27395         /**
27396              * @event select
27397              * Fires when a color is selected
27398              * @param {ColorPalette} this
27399              * @param {String} color The 6-digit color hex code (without the # symbol)
27400              */
27401         select: true
27402     });
27403
27404     if(this.handler){
27405         this.on("select", this.handler, this.scope, true);
27406     }
27407 };
27408 Roo.extend(Roo.ColorPalette, Roo.Component, {
27409     /**
27410      * @cfg {String} itemCls
27411      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27412      */
27413     itemCls : "x-color-palette",
27414     /**
27415      * @cfg {String} value
27416      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27417      * the hex codes are case-sensitive.
27418      */
27419     value : null,
27420     clickEvent:'click',
27421     // private
27422     ctype: "Roo.ColorPalette",
27423
27424     /**
27425      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27426      */
27427     allowReselect : false,
27428
27429     /**
27430      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27431      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27432      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27433      * of colors with the width setting until the box is symmetrical.</p>
27434      * <p>You can override individual colors if needed:</p>
27435      * <pre><code>
27436 var cp = new Roo.ColorPalette();
27437 cp.colors[0] = "FF0000";  // change the first box to red
27438 </code></pre>
27439
27440 Or you can provide a custom array of your own for complete control:
27441 <pre><code>
27442 var cp = new Roo.ColorPalette();
27443 cp.colors = ["000000", "993300", "333300"];
27444 </code></pre>
27445      * @type Array
27446      */
27447     colors : [
27448         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27449         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27450         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27451         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27452         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27453     ],
27454
27455     // private
27456     onRender : function(container, position){
27457         var t = new Roo.MasterTemplate(
27458             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27459         );
27460         var c = this.colors;
27461         for(var i = 0, len = c.length; i < len; i++){
27462             t.add([c[i]]);
27463         }
27464         var el = document.createElement("div");
27465         el.className = this.itemCls;
27466         t.overwrite(el);
27467         container.dom.insertBefore(el, position);
27468         this.el = Roo.get(el);
27469         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27470         if(this.clickEvent != 'click'){
27471             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27472         }
27473     },
27474
27475     // private
27476     afterRender : function(){
27477         Roo.ColorPalette.superclass.afterRender.call(this);
27478         if(this.value){
27479             var s = this.value;
27480             this.value = null;
27481             this.select(s);
27482         }
27483     },
27484
27485     // private
27486     handleClick : function(e, t){
27487         e.preventDefault();
27488         if(!this.disabled){
27489             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27490             this.select(c.toUpperCase());
27491         }
27492     },
27493
27494     /**
27495      * Selects the specified color in the palette (fires the select event)
27496      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27497      */
27498     select : function(color){
27499         color = color.replace("#", "");
27500         if(color != this.value || this.allowReselect){
27501             var el = this.el;
27502             if(this.value){
27503                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27504             }
27505             el.child("a.color-"+color).addClass("x-color-palette-sel");
27506             this.value = color;
27507             this.fireEvent("select", this, color);
27508         }
27509     }
27510 });/*
27511  * Based on:
27512  * Ext JS Library 1.1.1
27513  * Copyright(c) 2006-2007, Ext JS, LLC.
27514  *
27515  * Originally Released Under LGPL - original licence link has changed is not relivant.
27516  *
27517  * Fork - LGPL
27518  * <script type="text/javascript">
27519  */
27520  
27521 /**
27522  * @class Roo.DatePicker
27523  * @extends Roo.Component
27524  * Simple date picker class.
27525  * @constructor
27526  * Create a new DatePicker
27527  * @param {Object} config The config object
27528  */
27529 Roo.DatePicker = function(config){
27530     Roo.DatePicker.superclass.constructor.call(this, config);
27531
27532     this.value = config && config.value ?
27533                  config.value.clearTime() : new Date().clearTime();
27534
27535     this.addEvents({
27536         /**
27537              * @event select
27538              * Fires when a date is selected
27539              * @param {DatePicker} this
27540              * @param {Date} date The selected date
27541              */
27542         'select': true,
27543         /**
27544              * @event monthchange
27545              * Fires when the displayed month changes 
27546              * @param {DatePicker} this
27547              * @param {Date} date The selected month
27548              */
27549         'monthchange': true
27550     });
27551
27552     if(this.handler){
27553         this.on("select", this.handler,  this.scope || this);
27554     }
27555     // build the disabledDatesRE
27556     if(!this.disabledDatesRE && this.disabledDates){
27557         var dd = this.disabledDates;
27558         var re = "(?:";
27559         for(var i = 0; i < dd.length; i++){
27560             re += dd[i];
27561             if(i != dd.length-1) {
27562                 re += "|";
27563             }
27564         }
27565         this.disabledDatesRE = new RegExp(re + ")");
27566     }
27567 };
27568
27569 Roo.extend(Roo.DatePicker, Roo.Component, {
27570     /**
27571      * @cfg {String} todayText
27572      * The text to display on the button that selects the current date (defaults to "Today")
27573      */
27574     todayText : "Today",
27575     /**
27576      * @cfg {String} okText
27577      * The text to display on the ok button
27578      */
27579     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27580     /**
27581      * @cfg {String} cancelText
27582      * The text to display on the cancel button
27583      */
27584     cancelText : "Cancel",
27585     /**
27586      * @cfg {String} todayTip
27587      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27588      */
27589     todayTip : "{0} (Spacebar)",
27590     /**
27591      * @cfg {Date} minDate
27592      * Minimum allowable date (JavaScript date object, defaults to null)
27593      */
27594     minDate : null,
27595     /**
27596      * @cfg {Date} maxDate
27597      * Maximum allowable date (JavaScript date object, defaults to null)
27598      */
27599     maxDate : null,
27600     /**
27601      * @cfg {String} minText
27602      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27603      */
27604     minText : "This date is before the minimum date",
27605     /**
27606      * @cfg {String} maxText
27607      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27608      */
27609     maxText : "This date is after the maximum date",
27610     /**
27611      * @cfg {String} format
27612      * The default date format string which can be overriden for localization support.  The format must be
27613      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27614      */
27615     format : "m/d/y",
27616     /**
27617      * @cfg {Array} disabledDays
27618      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27619      */
27620     disabledDays : null,
27621     /**
27622      * @cfg {String} disabledDaysText
27623      * The tooltip to display when the date falls on a disabled day (defaults to "")
27624      */
27625     disabledDaysText : "",
27626     /**
27627      * @cfg {RegExp} disabledDatesRE
27628      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27629      */
27630     disabledDatesRE : null,
27631     /**
27632      * @cfg {String} disabledDatesText
27633      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27634      */
27635     disabledDatesText : "",
27636     /**
27637      * @cfg {Boolean} constrainToViewport
27638      * True to constrain the date picker to the viewport (defaults to true)
27639      */
27640     constrainToViewport : true,
27641     /**
27642      * @cfg {Array} monthNames
27643      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27644      */
27645     monthNames : Date.monthNames,
27646     /**
27647      * @cfg {Array} dayNames
27648      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27649      */
27650     dayNames : Date.dayNames,
27651     /**
27652      * @cfg {String} nextText
27653      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27654      */
27655     nextText: 'Next Month (Control+Right)',
27656     /**
27657      * @cfg {String} prevText
27658      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27659      */
27660     prevText: 'Previous Month (Control+Left)',
27661     /**
27662      * @cfg {String} monthYearText
27663      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27664      */
27665     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27666     /**
27667      * @cfg {Number} startDay
27668      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27669      */
27670     startDay : 0,
27671     /**
27672      * @cfg {Bool} showClear
27673      * Show a clear button (usefull for date form elements that can be blank.)
27674      */
27675     
27676     showClear: false,
27677     
27678     /**
27679      * Sets the value of the date field
27680      * @param {Date} value The date to set
27681      */
27682     setValue : function(value){
27683         var old = this.value;
27684         
27685         if (typeof(value) == 'string') {
27686          
27687             value = Date.parseDate(value, this.format);
27688         }
27689         if (!value) {
27690             value = new Date();
27691         }
27692         
27693         this.value = value.clearTime(true);
27694         if(this.el){
27695             this.update(this.value);
27696         }
27697     },
27698
27699     /**
27700      * Gets the current selected value of the date field
27701      * @return {Date} The selected date
27702      */
27703     getValue : function(){
27704         return this.value;
27705     },
27706
27707     // private
27708     focus : function(){
27709         if(this.el){
27710             this.update(this.activeDate);
27711         }
27712     },
27713
27714     // privateval
27715     onRender : function(container, position){
27716         
27717         var m = [
27718              '<table cellspacing="0">',
27719                 '<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>',
27720                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27721         var dn = this.dayNames;
27722         for(var i = 0; i < 7; i++){
27723             var d = this.startDay+i;
27724             if(d > 6){
27725                 d = d-7;
27726             }
27727             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27728         }
27729         m[m.length] = "</tr></thead><tbody><tr>";
27730         for(var i = 0; i < 42; i++) {
27731             if(i % 7 == 0 && i != 0){
27732                 m[m.length] = "</tr><tr>";
27733             }
27734             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27735         }
27736         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27737             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27738
27739         var el = document.createElement("div");
27740         el.className = "x-date-picker";
27741         el.innerHTML = m.join("");
27742
27743         container.dom.insertBefore(el, position);
27744
27745         this.el = Roo.get(el);
27746         this.eventEl = Roo.get(el.firstChild);
27747
27748         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27749             handler: this.showPrevMonth,
27750             scope: this,
27751             preventDefault:true,
27752             stopDefault:true
27753         });
27754
27755         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27756             handler: this.showNextMonth,
27757             scope: this,
27758             preventDefault:true,
27759             stopDefault:true
27760         });
27761
27762         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27763
27764         this.monthPicker = this.el.down('div.x-date-mp');
27765         this.monthPicker.enableDisplayMode('block');
27766         
27767         var kn = new Roo.KeyNav(this.eventEl, {
27768             "left" : function(e){
27769                 e.ctrlKey ?
27770                     this.showPrevMonth() :
27771                     this.update(this.activeDate.add("d", -1));
27772             },
27773
27774             "right" : function(e){
27775                 e.ctrlKey ?
27776                     this.showNextMonth() :
27777                     this.update(this.activeDate.add("d", 1));
27778             },
27779
27780             "up" : function(e){
27781                 e.ctrlKey ?
27782                     this.showNextYear() :
27783                     this.update(this.activeDate.add("d", -7));
27784             },
27785
27786             "down" : function(e){
27787                 e.ctrlKey ?
27788                     this.showPrevYear() :
27789                     this.update(this.activeDate.add("d", 7));
27790             },
27791
27792             "pageUp" : function(e){
27793                 this.showNextMonth();
27794             },
27795
27796             "pageDown" : function(e){
27797                 this.showPrevMonth();
27798             },
27799
27800             "enter" : function(e){
27801                 e.stopPropagation();
27802                 return true;
27803             },
27804
27805             scope : this
27806         });
27807
27808         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27809
27810         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27811
27812         this.el.unselectable();
27813         
27814         this.cells = this.el.select("table.x-date-inner tbody td");
27815         this.textNodes = this.el.query("table.x-date-inner tbody span");
27816
27817         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27818             text: "&#160;",
27819             tooltip: this.monthYearText
27820         });
27821
27822         this.mbtn.on('click', this.showMonthPicker, this);
27823         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27824
27825
27826         var today = (new Date()).dateFormat(this.format);
27827         
27828         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27829         if (this.showClear) {
27830             baseTb.add( new Roo.Toolbar.Fill());
27831         }
27832         baseTb.add({
27833             text: String.format(this.todayText, today),
27834             tooltip: String.format(this.todayTip, today),
27835             handler: this.selectToday,
27836             scope: this
27837         });
27838         
27839         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27840             
27841         //});
27842         if (this.showClear) {
27843             
27844             baseTb.add( new Roo.Toolbar.Fill());
27845             baseTb.add({
27846                 text: '&#160;',
27847                 cls: 'x-btn-icon x-btn-clear',
27848                 handler: function() {
27849                     //this.value = '';
27850                     this.fireEvent("select", this, '');
27851                 },
27852                 scope: this
27853             });
27854         }
27855         
27856         
27857         if(Roo.isIE){
27858             this.el.repaint();
27859         }
27860         this.update(this.value);
27861     },
27862
27863     createMonthPicker : function(){
27864         if(!this.monthPicker.dom.firstChild){
27865             var buf = ['<table border="0" cellspacing="0">'];
27866             for(var i = 0; i < 6; i++){
27867                 buf.push(
27868                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27869                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27870                     i == 0 ?
27871                     '<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>' :
27872                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27873                 );
27874             }
27875             buf.push(
27876                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27877                     this.okText,
27878                     '</button><button type="button" class="x-date-mp-cancel">',
27879                     this.cancelText,
27880                     '</button></td></tr>',
27881                 '</table>'
27882             );
27883             this.monthPicker.update(buf.join(''));
27884             this.monthPicker.on('click', this.onMonthClick, this);
27885             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27886
27887             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27888             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27889
27890             this.mpMonths.each(function(m, a, i){
27891                 i += 1;
27892                 if((i%2) == 0){
27893                     m.dom.xmonth = 5 + Math.round(i * .5);
27894                 }else{
27895                     m.dom.xmonth = Math.round((i-1) * .5);
27896                 }
27897             });
27898         }
27899     },
27900
27901     showMonthPicker : function(){
27902         this.createMonthPicker();
27903         var size = this.el.getSize();
27904         this.monthPicker.setSize(size);
27905         this.monthPicker.child('table').setSize(size);
27906
27907         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27908         this.updateMPMonth(this.mpSelMonth);
27909         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27910         this.updateMPYear(this.mpSelYear);
27911
27912         this.monthPicker.slideIn('t', {duration:.2});
27913     },
27914
27915     updateMPYear : function(y){
27916         this.mpyear = y;
27917         var ys = this.mpYears.elements;
27918         for(var i = 1; i <= 10; i++){
27919             var td = ys[i-1], y2;
27920             if((i%2) == 0){
27921                 y2 = y + Math.round(i * .5);
27922                 td.firstChild.innerHTML = y2;
27923                 td.xyear = y2;
27924             }else{
27925                 y2 = y - (5-Math.round(i * .5));
27926                 td.firstChild.innerHTML = y2;
27927                 td.xyear = y2;
27928             }
27929             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27930         }
27931     },
27932
27933     updateMPMonth : function(sm){
27934         this.mpMonths.each(function(m, a, i){
27935             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27936         });
27937     },
27938
27939     selectMPMonth: function(m){
27940         
27941     },
27942
27943     onMonthClick : function(e, t){
27944         e.stopEvent();
27945         var el = new Roo.Element(t), pn;
27946         if(el.is('button.x-date-mp-cancel')){
27947             this.hideMonthPicker();
27948         }
27949         else if(el.is('button.x-date-mp-ok')){
27950             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27951             this.hideMonthPicker();
27952         }
27953         else if(pn = el.up('td.x-date-mp-month', 2)){
27954             this.mpMonths.removeClass('x-date-mp-sel');
27955             pn.addClass('x-date-mp-sel');
27956             this.mpSelMonth = pn.dom.xmonth;
27957         }
27958         else if(pn = el.up('td.x-date-mp-year', 2)){
27959             this.mpYears.removeClass('x-date-mp-sel');
27960             pn.addClass('x-date-mp-sel');
27961             this.mpSelYear = pn.dom.xyear;
27962         }
27963         else if(el.is('a.x-date-mp-prev')){
27964             this.updateMPYear(this.mpyear-10);
27965         }
27966         else if(el.is('a.x-date-mp-next')){
27967             this.updateMPYear(this.mpyear+10);
27968         }
27969     },
27970
27971     onMonthDblClick : function(e, t){
27972         e.stopEvent();
27973         var el = new Roo.Element(t), pn;
27974         if(pn = el.up('td.x-date-mp-month', 2)){
27975             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27976             this.hideMonthPicker();
27977         }
27978         else if(pn = el.up('td.x-date-mp-year', 2)){
27979             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27980             this.hideMonthPicker();
27981         }
27982     },
27983
27984     hideMonthPicker : function(disableAnim){
27985         if(this.monthPicker){
27986             if(disableAnim === true){
27987                 this.monthPicker.hide();
27988             }else{
27989                 this.monthPicker.slideOut('t', {duration:.2});
27990             }
27991         }
27992     },
27993
27994     // private
27995     showPrevMonth : function(e){
27996         this.update(this.activeDate.add("mo", -1));
27997     },
27998
27999     // private
28000     showNextMonth : function(e){
28001         this.update(this.activeDate.add("mo", 1));
28002     },
28003
28004     // private
28005     showPrevYear : function(){
28006         this.update(this.activeDate.add("y", -1));
28007     },
28008
28009     // private
28010     showNextYear : function(){
28011         this.update(this.activeDate.add("y", 1));
28012     },
28013
28014     // private
28015     handleMouseWheel : function(e){
28016         var delta = e.getWheelDelta();
28017         if(delta > 0){
28018             this.showPrevMonth();
28019             e.stopEvent();
28020         } else if(delta < 0){
28021             this.showNextMonth();
28022             e.stopEvent();
28023         }
28024     },
28025
28026     // private
28027     handleDateClick : function(e, t){
28028         e.stopEvent();
28029         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28030             this.setValue(new Date(t.dateValue));
28031             this.fireEvent("select", this, this.value);
28032         }
28033     },
28034
28035     // private
28036     selectToday : function(){
28037         this.setValue(new Date().clearTime());
28038         this.fireEvent("select", this, this.value);
28039     },
28040
28041     // private
28042     update : function(date)
28043     {
28044         var vd = this.activeDate;
28045         this.activeDate = date;
28046         if(vd && this.el){
28047             var t = date.getTime();
28048             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28049                 this.cells.removeClass("x-date-selected");
28050                 this.cells.each(function(c){
28051                    if(c.dom.firstChild.dateValue == t){
28052                        c.addClass("x-date-selected");
28053                        setTimeout(function(){
28054                             try{c.dom.firstChild.focus();}catch(e){}
28055                        }, 50);
28056                        return false;
28057                    }
28058                 });
28059                 return;
28060             }
28061         }
28062         
28063         var days = date.getDaysInMonth();
28064         var firstOfMonth = date.getFirstDateOfMonth();
28065         var startingPos = firstOfMonth.getDay()-this.startDay;
28066
28067         if(startingPos <= this.startDay){
28068             startingPos += 7;
28069         }
28070
28071         var pm = date.add("mo", -1);
28072         var prevStart = pm.getDaysInMonth()-startingPos;
28073
28074         var cells = this.cells.elements;
28075         var textEls = this.textNodes;
28076         days += startingPos;
28077
28078         // convert everything to numbers so it's fast
28079         var day = 86400000;
28080         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28081         var today = new Date().clearTime().getTime();
28082         var sel = date.clearTime().getTime();
28083         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28084         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28085         var ddMatch = this.disabledDatesRE;
28086         var ddText = this.disabledDatesText;
28087         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28088         var ddaysText = this.disabledDaysText;
28089         var format = this.format;
28090
28091         var setCellClass = function(cal, cell){
28092             cell.title = "";
28093             var t = d.getTime();
28094             cell.firstChild.dateValue = t;
28095             if(t == today){
28096                 cell.className += " x-date-today";
28097                 cell.title = cal.todayText;
28098             }
28099             if(t == sel){
28100                 cell.className += " x-date-selected";
28101                 setTimeout(function(){
28102                     try{cell.firstChild.focus();}catch(e){}
28103                 }, 50);
28104             }
28105             // disabling
28106             if(t < min) {
28107                 cell.className = " x-date-disabled";
28108                 cell.title = cal.minText;
28109                 return;
28110             }
28111             if(t > max) {
28112                 cell.className = " x-date-disabled";
28113                 cell.title = cal.maxText;
28114                 return;
28115             }
28116             if(ddays){
28117                 if(ddays.indexOf(d.getDay()) != -1){
28118                     cell.title = ddaysText;
28119                     cell.className = " x-date-disabled";
28120                 }
28121             }
28122             if(ddMatch && format){
28123                 var fvalue = d.dateFormat(format);
28124                 if(ddMatch.test(fvalue)){
28125                     cell.title = ddText.replace("%0", fvalue);
28126                     cell.className = " x-date-disabled";
28127                 }
28128             }
28129         };
28130
28131         var i = 0;
28132         for(; i < startingPos; i++) {
28133             textEls[i].innerHTML = (++prevStart);
28134             d.setDate(d.getDate()+1);
28135             cells[i].className = "x-date-prevday";
28136             setCellClass(this, cells[i]);
28137         }
28138         for(; i < days; i++){
28139             intDay = i - startingPos + 1;
28140             textEls[i].innerHTML = (intDay);
28141             d.setDate(d.getDate()+1);
28142             cells[i].className = "x-date-active";
28143             setCellClass(this, cells[i]);
28144         }
28145         var extraDays = 0;
28146         for(; i < 42; i++) {
28147              textEls[i].innerHTML = (++extraDays);
28148              d.setDate(d.getDate()+1);
28149              cells[i].className = "x-date-nextday";
28150              setCellClass(this, cells[i]);
28151         }
28152
28153         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28154         this.fireEvent('monthchange', this, date);
28155         
28156         if(!this.internalRender){
28157             var main = this.el.dom.firstChild;
28158             var w = main.offsetWidth;
28159             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28160             Roo.fly(main).setWidth(w);
28161             this.internalRender = true;
28162             // opera does not respect the auto grow header center column
28163             // then, after it gets a width opera refuses to recalculate
28164             // without a second pass
28165             if(Roo.isOpera && !this.secondPass){
28166                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28167                 this.secondPass = true;
28168                 this.update.defer(10, this, [date]);
28169             }
28170         }
28171         
28172         
28173     }
28174 });        /*
28175  * Based on:
28176  * Ext JS Library 1.1.1
28177  * Copyright(c) 2006-2007, Ext JS, LLC.
28178  *
28179  * Originally Released Under LGPL - original licence link has changed is not relivant.
28180  *
28181  * Fork - LGPL
28182  * <script type="text/javascript">
28183  */
28184 /**
28185  * @class Roo.TabPanel
28186  * @extends Roo.util.Observable
28187  * A lightweight tab container.
28188  * <br><br>
28189  * Usage:
28190  * <pre><code>
28191 // basic tabs 1, built from existing content
28192 var tabs = new Roo.TabPanel("tabs1");
28193 tabs.addTab("script", "View Script");
28194 tabs.addTab("markup", "View Markup");
28195 tabs.activate("script");
28196
28197 // more advanced tabs, built from javascript
28198 var jtabs = new Roo.TabPanel("jtabs");
28199 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28200
28201 // set up the UpdateManager
28202 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28203 var updater = tab2.getUpdateManager();
28204 updater.setDefaultUrl("ajax1.htm");
28205 tab2.on('activate', updater.refresh, updater, true);
28206
28207 // Use setUrl for Ajax loading
28208 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28209 tab3.setUrl("ajax2.htm", null, true);
28210
28211 // Disabled tab
28212 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28213 tab4.disable();
28214
28215 jtabs.activate("jtabs-1");
28216  * </code></pre>
28217  * @constructor
28218  * Create a new TabPanel.
28219  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28220  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28221  */
28222 Roo.TabPanel = function(container, config){
28223     /**
28224     * The container element for this TabPanel.
28225     * @type Roo.Element
28226     */
28227     this.el = Roo.get(container, true);
28228     if(config){
28229         if(typeof config == "boolean"){
28230             this.tabPosition = config ? "bottom" : "top";
28231         }else{
28232             Roo.apply(this, config);
28233         }
28234     }
28235     if(this.tabPosition == "bottom"){
28236         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28237         this.el.addClass("x-tabs-bottom");
28238     }
28239     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28240     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28241     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28242     if(Roo.isIE){
28243         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28244     }
28245     if(this.tabPosition != "bottom"){
28246         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28247          * @type Roo.Element
28248          */
28249         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28250         this.el.addClass("x-tabs-top");
28251     }
28252     this.items = [];
28253
28254     this.bodyEl.setStyle("position", "relative");
28255
28256     this.active = null;
28257     this.activateDelegate = this.activate.createDelegate(this);
28258
28259     this.addEvents({
28260         /**
28261          * @event tabchange
28262          * Fires when the active tab changes
28263          * @param {Roo.TabPanel} this
28264          * @param {Roo.TabPanelItem} activePanel The new active tab
28265          */
28266         "tabchange": true,
28267         /**
28268          * @event beforetabchange
28269          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28270          * @param {Roo.TabPanel} this
28271          * @param {Object} e Set cancel to true on this object to cancel the tab change
28272          * @param {Roo.TabPanelItem} tab The tab being changed to
28273          */
28274         "beforetabchange" : true
28275     });
28276
28277     Roo.EventManager.onWindowResize(this.onResize, this);
28278     this.cpad = this.el.getPadding("lr");
28279     this.hiddenCount = 0;
28280
28281
28282     // toolbar on the tabbar support...
28283     if (this.toolbar) {
28284         var tcfg = this.toolbar;
28285         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28286         this.toolbar = new Roo.Toolbar(tcfg);
28287         if (Roo.isSafari) {
28288             var tbl = tcfg.container.child('table', true);
28289             tbl.setAttribute('width', '100%');
28290         }
28291         
28292     }
28293    
28294
28295
28296     Roo.TabPanel.superclass.constructor.call(this);
28297 };
28298
28299 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28300     /*
28301      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28302      */
28303     tabPosition : "top",
28304     /*
28305      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28306      */
28307     currentTabWidth : 0,
28308     /*
28309      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28310      */
28311     minTabWidth : 40,
28312     /*
28313      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28314      */
28315     maxTabWidth : 250,
28316     /*
28317      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28318      */
28319     preferredTabWidth : 175,
28320     /*
28321      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28322      */
28323     resizeTabs : false,
28324     /*
28325      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28326      */
28327     monitorResize : true,
28328     /*
28329      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28330      */
28331     toolbar : false,
28332
28333     /**
28334      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28335      * @param {String} id The id of the div to use <b>or create</b>
28336      * @param {String} text The text for the tab
28337      * @param {String} content (optional) Content to put in the TabPanelItem body
28338      * @param {Boolean} closable (optional) True to create a close icon on the tab
28339      * @return {Roo.TabPanelItem} The created TabPanelItem
28340      */
28341     addTab : function(id, text, content, closable){
28342         var item = new Roo.TabPanelItem(this, id, text, closable);
28343         this.addTabItem(item);
28344         if(content){
28345             item.setContent(content);
28346         }
28347         return item;
28348     },
28349
28350     /**
28351      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28352      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28353      * @return {Roo.TabPanelItem}
28354      */
28355     getTab : function(id){
28356         return this.items[id];
28357     },
28358
28359     /**
28360      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28361      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28362      */
28363     hideTab : function(id){
28364         var t = this.items[id];
28365         if(!t.isHidden()){
28366            t.setHidden(true);
28367            this.hiddenCount++;
28368            this.autoSizeTabs();
28369         }
28370     },
28371
28372     /**
28373      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28374      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28375      */
28376     unhideTab : function(id){
28377         var t = this.items[id];
28378         if(t.isHidden()){
28379            t.setHidden(false);
28380            this.hiddenCount--;
28381            this.autoSizeTabs();
28382         }
28383     },
28384
28385     /**
28386      * Adds an existing {@link Roo.TabPanelItem}.
28387      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28388      */
28389     addTabItem : function(item){
28390         this.items[item.id] = item;
28391         this.items.push(item);
28392         if(this.resizeTabs){
28393            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28394            this.autoSizeTabs();
28395         }else{
28396             item.autoSize();
28397         }
28398     },
28399
28400     /**
28401      * Removes a {@link Roo.TabPanelItem}.
28402      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28403      */
28404     removeTab : function(id){
28405         var items = this.items;
28406         var tab = items[id];
28407         if(!tab) { return; }
28408         var index = items.indexOf(tab);
28409         if(this.active == tab && items.length > 1){
28410             var newTab = this.getNextAvailable(index);
28411             if(newTab) {
28412                 newTab.activate();
28413             }
28414         }
28415         this.stripEl.dom.removeChild(tab.pnode.dom);
28416         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28417             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28418         }
28419         items.splice(index, 1);
28420         delete this.items[tab.id];
28421         tab.fireEvent("close", tab);
28422         tab.purgeListeners();
28423         this.autoSizeTabs();
28424     },
28425
28426     getNextAvailable : function(start){
28427         var items = this.items;
28428         var index = start;
28429         // look for a next tab that will slide over to
28430         // replace the one being removed
28431         while(index < items.length){
28432             var item = items[++index];
28433             if(item && !item.isHidden()){
28434                 return item;
28435             }
28436         }
28437         // if one isn't found select the previous tab (on the left)
28438         index = start;
28439         while(index >= 0){
28440             var item = items[--index];
28441             if(item && !item.isHidden()){
28442                 return item;
28443             }
28444         }
28445         return null;
28446     },
28447
28448     /**
28449      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28450      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28451      */
28452     disableTab : function(id){
28453         var tab = this.items[id];
28454         if(tab && this.active != tab){
28455             tab.disable();
28456         }
28457     },
28458
28459     /**
28460      * Enables a {@link Roo.TabPanelItem} that is disabled.
28461      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28462      */
28463     enableTab : function(id){
28464         var tab = this.items[id];
28465         tab.enable();
28466     },
28467
28468     /**
28469      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28470      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28471      * @return {Roo.TabPanelItem} The TabPanelItem.
28472      */
28473     activate : function(id){
28474         var tab = this.items[id];
28475         if(!tab){
28476             return null;
28477         }
28478         if(tab == this.active || tab.disabled){
28479             return tab;
28480         }
28481         var e = {};
28482         this.fireEvent("beforetabchange", this, e, tab);
28483         if(e.cancel !== true && !tab.disabled){
28484             if(this.active){
28485                 this.active.hide();
28486             }
28487             this.active = this.items[id];
28488             this.active.show();
28489             this.fireEvent("tabchange", this, this.active);
28490         }
28491         return tab;
28492     },
28493
28494     /**
28495      * Gets the active {@link Roo.TabPanelItem}.
28496      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28497      */
28498     getActiveTab : function(){
28499         return this.active;
28500     },
28501
28502     /**
28503      * Updates the tab body element to fit the height of the container element
28504      * for overflow scrolling
28505      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28506      */
28507     syncHeight : function(targetHeight){
28508         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28509         var bm = this.bodyEl.getMargins();
28510         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28511         this.bodyEl.setHeight(newHeight);
28512         return newHeight;
28513     },
28514
28515     onResize : function(){
28516         if(this.monitorResize){
28517             this.autoSizeTabs();
28518         }
28519     },
28520
28521     /**
28522      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28523      */
28524     beginUpdate : function(){
28525         this.updating = true;
28526     },
28527
28528     /**
28529      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28530      */
28531     endUpdate : function(){
28532         this.updating = false;
28533         this.autoSizeTabs();
28534     },
28535
28536     /**
28537      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28538      */
28539     autoSizeTabs : function(){
28540         var count = this.items.length;
28541         var vcount = count - this.hiddenCount;
28542         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28543             return;
28544         }
28545         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28546         var availWidth = Math.floor(w / vcount);
28547         var b = this.stripBody;
28548         if(b.getWidth() > w){
28549             var tabs = this.items;
28550             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28551             if(availWidth < this.minTabWidth){
28552                 /*if(!this.sleft){    // incomplete scrolling code
28553                     this.createScrollButtons();
28554                 }
28555                 this.showScroll();
28556                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28557             }
28558         }else{
28559             if(this.currentTabWidth < this.preferredTabWidth){
28560                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28561             }
28562         }
28563     },
28564
28565     /**
28566      * Returns the number of tabs in this TabPanel.
28567      * @return {Number}
28568      */
28569      getCount : function(){
28570          return this.items.length;
28571      },
28572
28573     /**
28574      * Resizes all the tabs to the passed width
28575      * @param {Number} The new width
28576      */
28577     setTabWidth : function(width){
28578         this.currentTabWidth = width;
28579         for(var i = 0, len = this.items.length; i < len; i++) {
28580                 if(!this.items[i].isHidden()) {
28581                 this.items[i].setWidth(width);
28582             }
28583         }
28584     },
28585
28586     /**
28587      * Destroys this TabPanel
28588      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28589      */
28590     destroy : function(removeEl){
28591         Roo.EventManager.removeResizeListener(this.onResize, this);
28592         for(var i = 0, len = this.items.length; i < len; i++){
28593             this.items[i].purgeListeners();
28594         }
28595         if(removeEl === true){
28596             this.el.update("");
28597             this.el.remove();
28598         }
28599     }
28600 });
28601
28602 /**
28603  * @class Roo.TabPanelItem
28604  * @extends Roo.util.Observable
28605  * Represents an individual item (tab plus body) in a TabPanel.
28606  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28607  * @param {String} id The id of this TabPanelItem
28608  * @param {String} text The text for the tab of this TabPanelItem
28609  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28610  */
28611 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28612     /**
28613      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28614      * @type Roo.TabPanel
28615      */
28616     this.tabPanel = tabPanel;
28617     /**
28618      * The id for this TabPanelItem
28619      * @type String
28620      */
28621     this.id = id;
28622     /** @private */
28623     this.disabled = false;
28624     /** @private */
28625     this.text = text;
28626     /** @private */
28627     this.loaded = false;
28628     this.closable = closable;
28629
28630     /**
28631      * The body element for this TabPanelItem.
28632      * @type Roo.Element
28633      */
28634     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28635     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28636     this.bodyEl.setStyle("display", "block");
28637     this.bodyEl.setStyle("zoom", "1");
28638     this.hideAction();
28639
28640     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28641     /** @private */
28642     this.el = Roo.get(els.el, true);
28643     this.inner = Roo.get(els.inner, true);
28644     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28645     this.pnode = Roo.get(els.el.parentNode, true);
28646     this.el.on("mousedown", this.onTabMouseDown, this);
28647     this.el.on("click", this.onTabClick, this);
28648     /** @private */
28649     if(closable){
28650         var c = Roo.get(els.close, true);
28651         c.dom.title = this.closeText;
28652         c.addClassOnOver("close-over");
28653         c.on("click", this.closeClick, this);
28654      }
28655
28656     this.addEvents({
28657          /**
28658          * @event activate
28659          * Fires when this tab becomes the active tab.
28660          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28661          * @param {Roo.TabPanelItem} this
28662          */
28663         "activate": true,
28664         /**
28665          * @event beforeclose
28666          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28667          * @param {Roo.TabPanelItem} this
28668          * @param {Object} e Set cancel to true on this object to cancel the close.
28669          */
28670         "beforeclose": true,
28671         /**
28672          * @event close
28673          * Fires when this tab is closed.
28674          * @param {Roo.TabPanelItem} this
28675          */
28676          "close": true,
28677         /**
28678          * @event deactivate
28679          * Fires when this tab is no longer the active tab.
28680          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28681          * @param {Roo.TabPanelItem} this
28682          */
28683          "deactivate" : true
28684     });
28685     this.hidden = false;
28686
28687     Roo.TabPanelItem.superclass.constructor.call(this);
28688 };
28689
28690 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28691     purgeListeners : function(){
28692        Roo.util.Observable.prototype.purgeListeners.call(this);
28693        this.el.removeAllListeners();
28694     },
28695     /**
28696      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28697      */
28698     show : function(){
28699         this.pnode.addClass("on");
28700         this.showAction();
28701         if(Roo.isOpera){
28702             this.tabPanel.stripWrap.repaint();
28703         }
28704         this.fireEvent("activate", this.tabPanel, this);
28705     },
28706
28707     /**
28708      * Returns true if this tab is the active tab.
28709      * @return {Boolean}
28710      */
28711     isActive : function(){
28712         return this.tabPanel.getActiveTab() == this;
28713     },
28714
28715     /**
28716      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28717      */
28718     hide : function(){
28719         this.pnode.removeClass("on");
28720         this.hideAction();
28721         this.fireEvent("deactivate", this.tabPanel, this);
28722     },
28723
28724     hideAction : function(){
28725         this.bodyEl.hide();
28726         this.bodyEl.setStyle("position", "absolute");
28727         this.bodyEl.setLeft("-20000px");
28728         this.bodyEl.setTop("-20000px");
28729     },
28730
28731     showAction : function(){
28732         this.bodyEl.setStyle("position", "relative");
28733         this.bodyEl.setTop("");
28734         this.bodyEl.setLeft("");
28735         this.bodyEl.show();
28736     },
28737
28738     /**
28739      * Set the tooltip for the tab.
28740      * @param {String} tooltip The tab's tooltip
28741      */
28742     setTooltip : function(text){
28743         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28744             this.textEl.dom.qtip = text;
28745             this.textEl.dom.removeAttribute('title');
28746         }else{
28747             this.textEl.dom.title = text;
28748         }
28749     },
28750
28751     onTabClick : function(e){
28752         e.preventDefault();
28753         this.tabPanel.activate(this.id);
28754     },
28755
28756     onTabMouseDown : function(e){
28757         e.preventDefault();
28758         this.tabPanel.activate(this.id);
28759     },
28760
28761     getWidth : function(){
28762         return this.inner.getWidth();
28763     },
28764
28765     setWidth : function(width){
28766         var iwidth = width - this.pnode.getPadding("lr");
28767         this.inner.setWidth(iwidth);
28768         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28769         this.pnode.setWidth(width);
28770     },
28771
28772     /**
28773      * Show or hide the tab
28774      * @param {Boolean} hidden True to hide or false to show.
28775      */
28776     setHidden : function(hidden){
28777         this.hidden = hidden;
28778         this.pnode.setStyle("display", hidden ? "none" : "");
28779     },
28780
28781     /**
28782      * Returns true if this tab is "hidden"
28783      * @return {Boolean}
28784      */
28785     isHidden : function(){
28786         return this.hidden;
28787     },
28788
28789     /**
28790      * Returns the text for this tab
28791      * @return {String}
28792      */
28793     getText : function(){
28794         return this.text;
28795     },
28796
28797     autoSize : function(){
28798         //this.el.beginMeasure();
28799         this.textEl.setWidth(1);
28800         /*
28801          *  #2804 [new] Tabs in Roojs
28802          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28803          */
28804         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28805         //this.el.endMeasure();
28806     },
28807
28808     /**
28809      * Sets the text for the tab (Note: this also sets the tooltip text)
28810      * @param {String} text The tab's text and tooltip
28811      */
28812     setText : function(text){
28813         this.text = text;
28814         this.textEl.update(text);
28815         this.setTooltip(text);
28816         if(!this.tabPanel.resizeTabs){
28817             this.autoSize();
28818         }
28819     },
28820     /**
28821      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28822      */
28823     activate : function(){
28824         this.tabPanel.activate(this.id);
28825     },
28826
28827     /**
28828      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28829      */
28830     disable : function(){
28831         if(this.tabPanel.active != this){
28832             this.disabled = true;
28833             this.pnode.addClass("disabled");
28834         }
28835     },
28836
28837     /**
28838      * Enables this TabPanelItem if it was previously disabled.
28839      */
28840     enable : function(){
28841         this.disabled = false;
28842         this.pnode.removeClass("disabled");
28843     },
28844
28845     /**
28846      * Sets the content for this TabPanelItem.
28847      * @param {String} content The content
28848      * @param {Boolean} loadScripts true to look for and load scripts
28849      */
28850     setContent : function(content, loadScripts){
28851         this.bodyEl.update(content, loadScripts);
28852     },
28853
28854     /**
28855      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28856      * @return {Roo.UpdateManager} The UpdateManager
28857      */
28858     getUpdateManager : function(){
28859         return this.bodyEl.getUpdateManager();
28860     },
28861
28862     /**
28863      * Set a URL to be used to load the content for this TabPanelItem.
28864      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28865      * @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)
28866      * @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)
28867      * @return {Roo.UpdateManager} The UpdateManager
28868      */
28869     setUrl : function(url, params, loadOnce){
28870         if(this.refreshDelegate){
28871             this.un('activate', this.refreshDelegate);
28872         }
28873         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28874         this.on("activate", this.refreshDelegate);
28875         return this.bodyEl.getUpdateManager();
28876     },
28877
28878     /** @private */
28879     _handleRefresh : function(url, params, loadOnce){
28880         if(!loadOnce || !this.loaded){
28881             var updater = this.bodyEl.getUpdateManager();
28882             updater.update(url, params, this._setLoaded.createDelegate(this));
28883         }
28884     },
28885
28886     /**
28887      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28888      *   Will fail silently if the setUrl method has not been called.
28889      *   This does not activate the panel, just updates its content.
28890      */
28891     refresh : function(){
28892         if(this.refreshDelegate){
28893            this.loaded = false;
28894            this.refreshDelegate();
28895         }
28896     },
28897
28898     /** @private */
28899     _setLoaded : function(){
28900         this.loaded = true;
28901     },
28902
28903     /** @private */
28904     closeClick : function(e){
28905         var o = {};
28906         e.stopEvent();
28907         this.fireEvent("beforeclose", this, o);
28908         if(o.cancel !== true){
28909             this.tabPanel.removeTab(this.id);
28910         }
28911     },
28912     /**
28913      * The text displayed in the tooltip for the close icon.
28914      * @type String
28915      */
28916     closeText : "Close this tab"
28917 });
28918
28919 /** @private */
28920 Roo.TabPanel.prototype.createStrip = function(container){
28921     var strip = document.createElement("div");
28922     strip.className = "x-tabs-wrap";
28923     container.appendChild(strip);
28924     return strip;
28925 };
28926 /** @private */
28927 Roo.TabPanel.prototype.createStripList = function(strip){
28928     // div wrapper for retard IE
28929     // returns the "tr" element.
28930     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28931         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28932         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28933     return strip.firstChild.firstChild.firstChild.firstChild;
28934 };
28935 /** @private */
28936 Roo.TabPanel.prototype.createBody = function(container){
28937     var body = document.createElement("div");
28938     Roo.id(body, "tab-body");
28939     Roo.fly(body).addClass("x-tabs-body");
28940     container.appendChild(body);
28941     return body;
28942 };
28943 /** @private */
28944 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28945     var body = Roo.getDom(id);
28946     if(!body){
28947         body = document.createElement("div");
28948         body.id = id;
28949     }
28950     Roo.fly(body).addClass("x-tabs-item-body");
28951     bodyEl.insertBefore(body, bodyEl.firstChild);
28952     return body;
28953 };
28954 /** @private */
28955 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28956     var td = document.createElement("td");
28957     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28958     //stripEl.appendChild(td);
28959     if(closable){
28960         td.className = "x-tabs-closable";
28961         if(!this.closeTpl){
28962             this.closeTpl = new Roo.Template(
28963                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28964                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28965                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28966             );
28967         }
28968         var el = this.closeTpl.overwrite(td, {"text": text});
28969         var close = el.getElementsByTagName("div")[0];
28970         var inner = el.getElementsByTagName("em")[0];
28971         return {"el": el, "close": close, "inner": inner};
28972     } else {
28973         if(!this.tabTpl){
28974             this.tabTpl = new Roo.Template(
28975                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28976                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28977             );
28978         }
28979         var el = this.tabTpl.overwrite(td, {"text": text});
28980         var inner = el.getElementsByTagName("em")[0];
28981         return {"el": el, "inner": inner};
28982     }
28983 };/*
28984  * Based on:
28985  * Ext JS Library 1.1.1
28986  * Copyright(c) 2006-2007, Ext JS, LLC.
28987  *
28988  * Originally Released Under LGPL - original licence link has changed is not relivant.
28989  *
28990  * Fork - LGPL
28991  * <script type="text/javascript">
28992  */
28993
28994 /**
28995  * @class Roo.Button
28996  * @extends Roo.util.Observable
28997  * Simple Button class
28998  * @cfg {String} text The button text
28999  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29000  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29001  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29002  * @cfg {Object} scope The scope of the handler
29003  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29004  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29005  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29006  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29007  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29008  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29009    applies if enableToggle = true)
29010  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29011  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29012   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29013  * @constructor
29014  * Create a new button
29015  * @param {Object} config The config object
29016  */
29017 Roo.Button = function(renderTo, config)
29018 {
29019     if (!config) {
29020         config = renderTo;
29021         renderTo = config.renderTo || false;
29022     }
29023     
29024     Roo.apply(this, config);
29025     this.addEvents({
29026         /**
29027              * @event click
29028              * Fires when this button is clicked
29029              * @param {Button} this
29030              * @param {EventObject} e The click event
29031              */
29032             "click" : true,
29033         /**
29034              * @event toggle
29035              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29036              * @param {Button} this
29037              * @param {Boolean} pressed
29038              */
29039             "toggle" : true,
29040         /**
29041              * @event mouseover
29042              * Fires when the mouse hovers over the button
29043              * @param {Button} this
29044              * @param {Event} e The event object
29045              */
29046         'mouseover' : true,
29047         /**
29048              * @event mouseout
29049              * Fires when the mouse exits the button
29050              * @param {Button} this
29051              * @param {Event} e The event object
29052              */
29053         'mouseout': true,
29054          /**
29055              * @event render
29056              * Fires when the button is rendered
29057              * @param {Button} this
29058              */
29059         'render': true
29060     });
29061     if(this.menu){
29062         this.menu = Roo.menu.MenuMgr.get(this.menu);
29063     }
29064     // register listeners first!!  - so render can be captured..
29065     Roo.util.Observable.call(this);
29066     if(renderTo){
29067         this.render(renderTo);
29068     }
29069     
29070   
29071 };
29072
29073 Roo.extend(Roo.Button, Roo.util.Observable, {
29074     /**
29075      * 
29076      */
29077     
29078     /**
29079      * Read-only. True if this button is hidden
29080      * @type Boolean
29081      */
29082     hidden : false,
29083     /**
29084      * Read-only. True if this button is disabled
29085      * @type Boolean
29086      */
29087     disabled : false,
29088     /**
29089      * Read-only. True if this button is pressed (only if enableToggle = true)
29090      * @type Boolean
29091      */
29092     pressed : false,
29093
29094     /**
29095      * @cfg {Number} tabIndex 
29096      * The DOM tabIndex for this button (defaults to undefined)
29097      */
29098     tabIndex : undefined,
29099
29100     /**
29101      * @cfg {Boolean} enableToggle
29102      * True to enable pressed/not pressed toggling (defaults to false)
29103      */
29104     enableToggle: false,
29105     /**
29106      * @cfg {Mixed} menu
29107      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29108      */
29109     menu : undefined,
29110     /**
29111      * @cfg {String} menuAlign
29112      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29113      */
29114     menuAlign : "tl-bl?",
29115
29116     /**
29117      * @cfg {String} iconCls
29118      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29119      */
29120     iconCls : undefined,
29121     /**
29122      * @cfg {String} type
29123      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29124      */
29125     type : 'button',
29126
29127     // private
29128     menuClassTarget: 'tr',
29129
29130     /**
29131      * @cfg {String} clickEvent
29132      * The type of event to map to the button's event handler (defaults to 'click')
29133      */
29134     clickEvent : 'click',
29135
29136     /**
29137      * @cfg {Boolean} handleMouseEvents
29138      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29139      */
29140     handleMouseEvents : true,
29141
29142     /**
29143      * @cfg {String} tooltipType
29144      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29145      */
29146     tooltipType : 'qtip',
29147
29148     /**
29149      * @cfg {String} cls
29150      * A CSS class to apply to the button's main element.
29151      */
29152     
29153     /**
29154      * @cfg {Roo.Template} template (Optional)
29155      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29156      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29157      * require code modifications if required elements (e.g. a button) aren't present.
29158      */
29159
29160     // private
29161     render : function(renderTo){
29162         var btn;
29163         if(this.hideParent){
29164             this.parentEl = Roo.get(renderTo);
29165         }
29166         if(!this.dhconfig){
29167             if(!this.template){
29168                 if(!Roo.Button.buttonTemplate){
29169                     // hideous table template
29170                     Roo.Button.buttonTemplate = new Roo.Template(
29171                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29172                         '<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>',
29173                         "</tr></tbody></table>");
29174                 }
29175                 this.template = Roo.Button.buttonTemplate;
29176             }
29177             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29178             var btnEl = btn.child("button:first");
29179             btnEl.on('focus', this.onFocus, this);
29180             btnEl.on('blur', this.onBlur, this);
29181             if(this.cls){
29182                 btn.addClass(this.cls);
29183             }
29184             if(this.icon){
29185                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29186             }
29187             if(this.iconCls){
29188                 btnEl.addClass(this.iconCls);
29189                 if(!this.cls){
29190                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29191                 }
29192             }
29193             if(this.tabIndex !== undefined){
29194                 btnEl.dom.tabIndex = this.tabIndex;
29195             }
29196             if(this.tooltip){
29197                 if(typeof this.tooltip == 'object'){
29198                     Roo.QuickTips.tips(Roo.apply({
29199                           target: btnEl.id
29200                     }, this.tooltip));
29201                 } else {
29202                     btnEl.dom[this.tooltipType] = this.tooltip;
29203                 }
29204             }
29205         }else{
29206             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29207         }
29208         this.el = btn;
29209         if(this.id){
29210             this.el.dom.id = this.el.id = this.id;
29211         }
29212         if(this.menu){
29213             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29214             this.menu.on("show", this.onMenuShow, this);
29215             this.menu.on("hide", this.onMenuHide, this);
29216         }
29217         btn.addClass("x-btn");
29218         if(Roo.isIE && !Roo.isIE7){
29219             this.autoWidth.defer(1, this);
29220         }else{
29221             this.autoWidth();
29222         }
29223         if(this.handleMouseEvents){
29224             btn.on("mouseover", this.onMouseOver, this);
29225             btn.on("mouseout", this.onMouseOut, this);
29226             btn.on("mousedown", this.onMouseDown, this);
29227         }
29228         btn.on(this.clickEvent, this.onClick, this);
29229         //btn.on("mouseup", this.onMouseUp, this);
29230         if(this.hidden){
29231             this.hide();
29232         }
29233         if(this.disabled){
29234             this.disable();
29235         }
29236         Roo.ButtonToggleMgr.register(this);
29237         if(this.pressed){
29238             this.el.addClass("x-btn-pressed");
29239         }
29240         if(this.repeat){
29241             var repeater = new Roo.util.ClickRepeater(btn,
29242                 typeof this.repeat == "object" ? this.repeat : {}
29243             );
29244             repeater.on("click", this.onClick,  this);
29245         }
29246         
29247         this.fireEvent('render', this);
29248         
29249     },
29250     /**
29251      * Returns the button's underlying element
29252      * @return {Roo.Element} The element
29253      */
29254     getEl : function(){
29255         return this.el;  
29256     },
29257     
29258     /**
29259      * Destroys this Button and removes any listeners.
29260      */
29261     destroy : function(){
29262         Roo.ButtonToggleMgr.unregister(this);
29263         this.el.removeAllListeners();
29264         this.purgeListeners();
29265         this.el.remove();
29266     },
29267
29268     // private
29269     autoWidth : function(){
29270         if(this.el){
29271             this.el.setWidth("auto");
29272             if(Roo.isIE7 && Roo.isStrict){
29273                 var ib = this.el.child('button');
29274                 if(ib && ib.getWidth() > 20){
29275                     ib.clip();
29276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29277                 }
29278             }
29279             if(this.minWidth){
29280                 if(this.hidden){
29281                     this.el.beginMeasure();
29282                 }
29283                 if(this.el.getWidth() < this.minWidth){
29284                     this.el.setWidth(this.minWidth);
29285                 }
29286                 if(this.hidden){
29287                     this.el.endMeasure();
29288                 }
29289             }
29290         }
29291     },
29292
29293     /**
29294      * Assigns this button's click handler
29295      * @param {Function} handler The function to call when the button is clicked
29296      * @param {Object} scope (optional) Scope for the function passed in
29297      */
29298     setHandler : function(handler, scope){
29299         this.handler = handler;
29300         this.scope = scope;  
29301     },
29302     
29303     /**
29304      * Sets this button's text
29305      * @param {String} text The button text
29306      */
29307     setText : function(text){
29308         this.text = text;
29309         if(this.el){
29310             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29311         }
29312         this.autoWidth();
29313     },
29314     
29315     /**
29316      * Gets the text for this button
29317      * @return {String} The button text
29318      */
29319     getText : function(){
29320         return this.text;  
29321     },
29322     
29323     /**
29324      * Show this button
29325      */
29326     show: function(){
29327         this.hidden = false;
29328         if(this.el){
29329             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29330         }
29331     },
29332     
29333     /**
29334      * Hide this button
29335      */
29336     hide: function(){
29337         this.hidden = true;
29338         if(this.el){
29339             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29340         }
29341     },
29342     
29343     /**
29344      * Convenience function for boolean show/hide
29345      * @param {Boolean} visible True to show, false to hide
29346      */
29347     setVisible: function(visible){
29348         if(visible) {
29349             this.show();
29350         }else{
29351             this.hide();
29352         }
29353     },
29354     
29355     /**
29356      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29357      * @param {Boolean} state (optional) Force a particular state
29358      */
29359     toggle : function(state){
29360         state = state === undefined ? !this.pressed : state;
29361         if(state != this.pressed){
29362             if(state){
29363                 this.el.addClass("x-btn-pressed");
29364                 this.pressed = true;
29365                 this.fireEvent("toggle", this, true);
29366             }else{
29367                 this.el.removeClass("x-btn-pressed");
29368                 this.pressed = false;
29369                 this.fireEvent("toggle", this, false);
29370             }
29371             if(this.toggleHandler){
29372                 this.toggleHandler.call(this.scope || this, this, state);
29373             }
29374         }
29375     },
29376     
29377     /**
29378      * Focus the button
29379      */
29380     focus : function(){
29381         this.el.child('button:first').focus();
29382     },
29383     
29384     /**
29385      * Disable this button
29386      */
29387     disable : function(){
29388         if(this.el){
29389             this.el.addClass("x-btn-disabled");
29390         }
29391         this.disabled = true;
29392     },
29393     
29394     /**
29395      * Enable this button
29396      */
29397     enable : function(){
29398         if(this.el){
29399             this.el.removeClass("x-btn-disabled");
29400         }
29401         this.disabled = false;
29402     },
29403
29404     /**
29405      * Convenience function for boolean enable/disable
29406      * @param {Boolean} enabled True to enable, false to disable
29407      */
29408     setDisabled : function(v){
29409         this[v !== true ? "enable" : "disable"]();
29410     },
29411
29412     // private
29413     onClick : function(e)
29414     {
29415         if(e){
29416             e.preventDefault();
29417         }
29418         if(e.button != 0){
29419             return;
29420         }
29421         if(!this.disabled){
29422             if(this.enableToggle){
29423                 this.toggle();
29424             }
29425             if(this.menu && !this.menu.isVisible()){
29426                 this.menu.show(this.el, this.menuAlign);
29427             }
29428             this.fireEvent("click", this, e);
29429             if(this.handler){
29430                 this.el.removeClass("x-btn-over");
29431                 this.handler.call(this.scope || this, this, e);
29432             }
29433         }
29434     },
29435     // private
29436     onMouseOver : function(e){
29437         if(!this.disabled){
29438             this.el.addClass("x-btn-over");
29439             this.fireEvent('mouseover', this, e);
29440         }
29441     },
29442     // private
29443     onMouseOut : function(e){
29444         if(!e.within(this.el,  true)){
29445             this.el.removeClass("x-btn-over");
29446             this.fireEvent('mouseout', this, e);
29447         }
29448     },
29449     // private
29450     onFocus : function(e){
29451         if(!this.disabled){
29452             this.el.addClass("x-btn-focus");
29453         }
29454     },
29455     // private
29456     onBlur : function(e){
29457         this.el.removeClass("x-btn-focus");
29458     },
29459     // private
29460     onMouseDown : function(e){
29461         if(!this.disabled && e.button == 0){
29462             this.el.addClass("x-btn-click");
29463             Roo.get(document).on('mouseup', this.onMouseUp, this);
29464         }
29465     },
29466     // private
29467     onMouseUp : function(e){
29468         if(e.button == 0){
29469             this.el.removeClass("x-btn-click");
29470             Roo.get(document).un('mouseup', this.onMouseUp, this);
29471         }
29472     },
29473     // private
29474     onMenuShow : function(e){
29475         this.el.addClass("x-btn-menu-active");
29476     },
29477     // private
29478     onMenuHide : function(e){
29479         this.el.removeClass("x-btn-menu-active");
29480     }   
29481 });
29482
29483 // Private utility class used by Button
29484 Roo.ButtonToggleMgr = function(){
29485    var groups = {};
29486    
29487    function toggleGroup(btn, state){
29488        if(state){
29489            var g = groups[btn.toggleGroup];
29490            for(var i = 0, l = g.length; i < l; i++){
29491                if(g[i] != btn){
29492                    g[i].toggle(false);
29493                }
29494            }
29495        }
29496    }
29497    
29498    return {
29499        register : function(btn){
29500            if(!btn.toggleGroup){
29501                return;
29502            }
29503            var g = groups[btn.toggleGroup];
29504            if(!g){
29505                g = groups[btn.toggleGroup] = [];
29506            }
29507            g.push(btn);
29508            btn.on("toggle", toggleGroup);
29509        },
29510        
29511        unregister : function(btn){
29512            if(!btn.toggleGroup){
29513                return;
29514            }
29515            var g = groups[btn.toggleGroup];
29516            if(g){
29517                g.remove(btn);
29518                btn.un("toggle", toggleGroup);
29519            }
29520        }
29521    };
29522 }();/*
29523  * Based on:
29524  * Ext JS Library 1.1.1
29525  * Copyright(c) 2006-2007, Ext JS, LLC.
29526  *
29527  * Originally Released Under LGPL - original licence link has changed is not relivant.
29528  *
29529  * Fork - LGPL
29530  * <script type="text/javascript">
29531  */
29532  
29533 /**
29534  * @class Roo.SplitButton
29535  * @extends Roo.Button
29536  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29537  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29538  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29539  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29540  * @cfg {String} arrowTooltip The title attribute of the arrow
29541  * @constructor
29542  * Create a new menu button
29543  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29544  * @param {Object} config The config object
29545  */
29546 Roo.SplitButton = function(renderTo, config){
29547     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29548     /**
29549      * @event arrowclick
29550      * Fires when this button's arrow is clicked
29551      * @param {SplitButton} this
29552      * @param {EventObject} e The click event
29553      */
29554     this.addEvents({"arrowclick":true});
29555 };
29556
29557 Roo.extend(Roo.SplitButton, Roo.Button, {
29558     render : function(renderTo){
29559         // this is one sweet looking template!
29560         var tpl = new Roo.Template(
29561             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29562             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29563             '<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>',
29564             "</tbody></table></td><td>",
29565             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29566             '<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>',
29567             "</tbody></table></td></tr></table>"
29568         );
29569         var btn = tpl.append(renderTo, [this.text, this.type], true);
29570         var btnEl = btn.child("button");
29571         if(this.cls){
29572             btn.addClass(this.cls);
29573         }
29574         if(this.icon){
29575             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29576         }
29577         if(this.iconCls){
29578             btnEl.addClass(this.iconCls);
29579             if(!this.cls){
29580                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29581             }
29582         }
29583         this.el = btn;
29584         if(this.handleMouseEvents){
29585             btn.on("mouseover", this.onMouseOver, this);
29586             btn.on("mouseout", this.onMouseOut, this);
29587             btn.on("mousedown", this.onMouseDown, this);
29588             btn.on("mouseup", this.onMouseUp, this);
29589         }
29590         btn.on(this.clickEvent, this.onClick, this);
29591         if(this.tooltip){
29592             if(typeof this.tooltip == 'object'){
29593                 Roo.QuickTips.tips(Roo.apply({
29594                       target: btnEl.id
29595                 }, this.tooltip));
29596             } else {
29597                 btnEl.dom[this.tooltipType] = this.tooltip;
29598             }
29599         }
29600         if(this.arrowTooltip){
29601             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29602         }
29603         if(this.hidden){
29604             this.hide();
29605         }
29606         if(this.disabled){
29607             this.disable();
29608         }
29609         if(this.pressed){
29610             this.el.addClass("x-btn-pressed");
29611         }
29612         if(Roo.isIE && !Roo.isIE7){
29613             this.autoWidth.defer(1, this);
29614         }else{
29615             this.autoWidth();
29616         }
29617         if(this.menu){
29618             this.menu.on("show", this.onMenuShow, this);
29619             this.menu.on("hide", this.onMenuHide, this);
29620         }
29621         this.fireEvent('render', this);
29622     },
29623
29624     // private
29625     autoWidth : function(){
29626         if(this.el){
29627             var tbl = this.el.child("table:first");
29628             var tbl2 = this.el.child("table:last");
29629             this.el.setWidth("auto");
29630             tbl.setWidth("auto");
29631             if(Roo.isIE7 && Roo.isStrict){
29632                 var ib = this.el.child('button:first');
29633                 if(ib && ib.getWidth() > 20){
29634                     ib.clip();
29635                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29636                 }
29637             }
29638             if(this.minWidth){
29639                 if(this.hidden){
29640                     this.el.beginMeasure();
29641                 }
29642                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29643                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29644                 }
29645                 if(this.hidden){
29646                     this.el.endMeasure();
29647                 }
29648             }
29649             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29650         } 
29651     },
29652     /**
29653      * Sets this button's click handler
29654      * @param {Function} handler The function to call when the button is clicked
29655      * @param {Object} scope (optional) Scope for the function passed above
29656      */
29657     setHandler : function(handler, scope){
29658         this.handler = handler;
29659         this.scope = scope;  
29660     },
29661     
29662     /**
29663      * Sets this button's arrow click handler
29664      * @param {Function} handler The function to call when the arrow is clicked
29665      * @param {Object} scope (optional) Scope for the function passed above
29666      */
29667     setArrowHandler : function(handler, scope){
29668         this.arrowHandler = handler;
29669         this.scope = scope;  
29670     },
29671     
29672     /**
29673      * Focus the button
29674      */
29675     focus : function(){
29676         if(this.el){
29677             this.el.child("button:first").focus();
29678         }
29679     },
29680
29681     // private
29682     onClick : function(e){
29683         e.preventDefault();
29684         if(!this.disabled){
29685             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29686                 if(this.menu && !this.menu.isVisible()){
29687                     this.menu.show(this.el, this.menuAlign);
29688                 }
29689                 this.fireEvent("arrowclick", this, e);
29690                 if(this.arrowHandler){
29691                     this.arrowHandler.call(this.scope || this, this, e);
29692                 }
29693             }else{
29694                 this.fireEvent("click", this, e);
29695                 if(this.handler){
29696                     this.handler.call(this.scope || this, this, e);
29697                 }
29698             }
29699         }
29700     },
29701     // private
29702     onMouseDown : function(e){
29703         if(!this.disabled){
29704             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29705         }
29706     },
29707     // private
29708     onMouseUp : function(e){
29709         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29710     }   
29711 });
29712
29713
29714 // backwards compat
29715 Roo.MenuButton = Roo.SplitButton;/*
29716  * Based on:
29717  * Ext JS Library 1.1.1
29718  * Copyright(c) 2006-2007, Ext JS, LLC.
29719  *
29720  * Originally Released Under LGPL - original licence link has changed is not relivant.
29721  *
29722  * Fork - LGPL
29723  * <script type="text/javascript">
29724  */
29725
29726 /**
29727  * @class Roo.Toolbar
29728  * Basic Toolbar class.
29729  * @constructor
29730  * Creates a new Toolbar
29731  * @param {Object} container The config object
29732  */ 
29733 Roo.Toolbar = function(container, buttons, config)
29734 {
29735     /// old consturctor format still supported..
29736     if(container instanceof Array){ // omit the container for later rendering
29737         buttons = container;
29738         config = buttons;
29739         container = null;
29740     }
29741     if (typeof(container) == 'object' && container.xtype) {
29742         config = container;
29743         container = config.container;
29744         buttons = config.buttons || []; // not really - use items!!
29745     }
29746     var xitems = [];
29747     if (config && config.items) {
29748         xitems = config.items;
29749         delete config.items;
29750     }
29751     Roo.apply(this, config);
29752     this.buttons = buttons;
29753     
29754     if(container){
29755         this.render(container);
29756     }
29757     this.xitems = xitems;
29758     Roo.each(xitems, function(b) {
29759         this.add(b);
29760     }, this);
29761     
29762 };
29763
29764 Roo.Toolbar.prototype = {
29765     /**
29766      * @cfg {Array} items
29767      * array of button configs or elements to add (will be converted to a MixedCollection)
29768      */
29769     
29770     /**
29771      * @cfg {String/HTMLElement/Element} container
29772      * The id or element that will contain the toolbar
29773      */
29774     // private
29775     render : function(ct){
29776         this.el = Roo.get(ct);
29777         if(this.cls){
29778             this.el.addClass(this.cls);
29779         }
29780         // using a table allows for vertical alignment
29781         // 100% width is needed by Safari...
29782         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29783         this.tr = this.el.child("tr", true);
29784         var autoId = 0;
29785         this.items = new Roo.util.MixedCollection(false, function(o){
29786             return o.id || ("item" + (++autoId));
29787         });
29788         if(this.buttons){
29789             this.add.apply(this, this.buttons);
29790             delete this.buttons;
29791         }
29792     },
29793
29794     /**
29795      * Adds element(s) to the toolbar -- this function takes a variable number of 
29796      * arguments of mixed type and adds them to the toolbar.
29797      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29798      * <ul>
29799      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29800      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29801      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29802      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29803      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29804      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29805      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29806      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29807      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29808      * </ul>
29809      * @param {Mixed} arg2
29810      * @param {Mixed} etc.
29811      */
29812     add : function(){
29813         var a = arguments, l = a.length;
29814         for(var i = 0; i < l; i++){
29815             this._add(a[i]);
29816         }
29817     },
29818     // private..
29819     _add : function(el) {
29820         
29821         if (el.xtype) {
29822             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29823         }
29824         
29825         if (el.applyTo){ // some kind of form field
29826             return this.addField(el);
29827         } 
29828         if (el.render){ // some kind of Toolbar.Item
29829             return this.addItem(el);
29830         }
29831         if (typeof el == "string"){ // string
29832             if(el == "separator" || el == "-"){
29833                 return this.addSeparator();
29834             }
29835             if (el == " "){
29836                 return this.addSpacer();
29837             }
29838             if(el == "->"){
29839                 return this.addFill();
29840             }
29841             return this.addText(el);
29842             
29843         }
29844         if(el.tagName){ // element
29845             return this.addElement(el);
29846         }
29847         if(typeof el == "object"){ // must be button config?
29848             return this.addButton(el);
29849         }
29850         // and now what?!?!
29851         return false;
29852         
29853     },
29854     
29855     /**
29856      * Add an Xtype element
29857      * @param {Object} xtype Xtype Object
29858      * @return {Object} created Object
29859      */
29860     addxtype : function(e){
29861         return this.add(e);  
29862     },
29863     
29864     /**
29865      * Returns the Element for this toolbar.
29866      * @return {Roo.Element}
29867      */
29868     getEl : function(){
29869         return this.el;  
29870     },
29871     
29872     /**
29873      * Adds a separator
29874      * @return {Roo.Toolbar.Item} The separator item
29875      */
29876     addSeparator : function(){
29877         return this.addItem(new Roo.Toolbar.Separator());
29878     },
29879
29880     /**
29881      * Adds a spacer element
29882      * @return {Roo.Toolbar.Spacer} The spacer item
29883      */
29884     addSpacer : function(){
29885         return this.addItem(new Roo.Toolbar.Spacer());
29886     },
29887
29888     /**
29889      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29890      * @return {Roo.Toolbar.Fill} The fill item
29891      */
29892     addFill : function(){
29893         return this.addItem(new Roo.Toolbar.Fill());
29894     },
29895
29896     /**
29897      * Adds any standard HTML element to the toolbar
29898      * @param {String/HTMLElement/Element} el The element or id of the element to add
29899      * @return {Roo.Toolbar.Item} The element's item
29900      */
29901     addElement : function(el){
29902         return this.addItem(new Roo.Toolbar.Item(el));
29903     },
29904     /**
29905      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29906      * @type Roo.util.MixedCollection  
29907      */
29908     items : false,
29909      
29910     /**
29911      * Adds any Toolbar.Item or subclass
29912      * @param {Roo.Toolbar.Item} item
29913      * @return {Roo.Toolbar.Item} The item
29914      */
29915     addItem : function(item){
29916         var td = this.nextBlock();
29917         item.render(td);
29918         this.items.add(item);
29919         return item;
29920     },
29921     
29922     /**
29923      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29924      * @param {Object/Array} config A button config or array of configs
29925      * @return {Roo.Toolbar.Button/Array}
29926      */
29927     addButton : function(config){
29928         if(config instanceof Array){
29929             var buttons = [];
29930             for(var i = 0, len = config.length; i < len; i++) {
29931                 buttons.push(this.addButton(config[i]));
29932             }
29933             return buttons;
29934         }
29935         var b = config;
29936         if(!(config instanceof Roo.Toolbar.Button)){
29937             b = config.split ?
29938                 new Roo.Toolbar.SplitButton(config) :
29939                 new Roo.Toolbar.Button(config);
29940         }
29941         var td = this.nextBlock();
29942         b.render(td);
29943         this.items.add(b);
29944         return b;
29945     },
29946     
29947     /**
29948      * Adds text to the toolbar
29949      * @param {String} text The text to add
29950      * @return {Roo.Toolbar.Item} The element's item
29951      */
29952     addText : function(text){
29953         return this.addItem(new Roo.Toolbar.TextItem(text));
29954     },
29955     
29956     /**
29957      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29958      * @param {Number} index The index where the item is to be inserted
29959      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29960      * @return {Roo.Toolbar.Button/Item}
29961      */
29962     insertButton : function(index, item){
29963         if(item instanceof Array){
29964             var buttons = [];
29965             for(var i = 0, len = item.length; i < len; i++) {
29966                buttons.push(this.insertButton(index + i, item[i]));
29967             }
29968             return buttons;
29969         }
29970         if (!(item instanceof Roo.Toolbar.Button)){
29971            item = new Roo.Toolbar.Button(item);
29972         }
29973         var td = document.createElement("td");
29974         this.tr.insertBefore(td, this.tr.childNodes[index]);
29975         item.render(td);
29976         this.items.insert(index, item);
29977         return item;
29978     },
29979     
29980     /**
29981      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29982      * @param {Object} config
29983      * @return {Roo.Toolbar.Item} The element's item
29984      */
29985     addDom : function(config, returnEl){
29986         var td = this.nextBlock();
29987         Roo.DomHelper.overwrite(td, config);
29988         var ti = new Roo.Toolbar.Item(td.firstChild);
29989         ti.render(td);
29990         this.items.add(ti);
29991         return ti;
29992     },
29993
29994     /**
29995      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29996      * @type Roo.util.MixedCollection  
29997      */
29998     fields : false,
29999     
30000     /**
30001      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30002      * Note: the field should not have been rendered yet. For a field that has already been
30003      * rendered, use {@link #addElement}.
30004      * @param {Roo.form.Field} field
30005      * @return {Roo.ToolbarItem}
30006      */
30007      
30008       
30009     addField : function(field) {
30010         if (!this.fields) {
30011             var autoId = 0;
30012             this.fields = new Roo.util.MixedCollection(false, function(o){
30013                 return o.id || ("item" + (++autoId));
30014             });
30015
30016         }
30017         
30018         var td = this.nextBlock();
30019         field.render(td);
30020         var ti = new Roo.Toolbar.Item(td.firstChild);
30021         ti.render(td);
30022         this.items.add(ti);
30023         this.fields.add(field);
30024         return ti;
30025     },
30026     /**
30027      * Hide the toolbar
30028      * @method hide
30029      */
30030      
30031       
30032     hide : function()
30033     {
30034         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30035         this.el.child('div').hide();
30036     },
30037     /**
30038      * Show the toolbar
30039      * @method show
30040      */
30041     show : function()
30042     {
30043         this.el.child('div').show();
30044     },
30045       
30046     // private
30047     nextBlock : function(){
30048         var td = document.createElement("td");
30049         this.tr.appendChild(td);
30050         return td;
30051     },
30052
30053     // private
30054     destroy : function(){
30055         if(this.items){ // rendered?
30056             Roo.destroy.apply(Roo, this.items.items);
30057         }
30058         if(this.fields){ // rendered?
30059             Roo.destroy.apply(Roo, this.fields.items);
30060         }
30061         Roo.Element.uncache(this.el, this.tr);
30062     }
30063 };
30064
30065 /**
30066  * @class Roo.Toolbar.Item
30067  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30068  * @constructor
30069  * Creates a new Item
30070  * @param {HTMLElement} el 
30071  */
30072 Roo.Toolbar.Item = function(el){
30073     var cfg = {};
30074     if (typeof (el.xtype) != 'undefined') {
30075         cfg = el;
30076         el = cfg.el;
30077     }
30078     
30079     this.el = Roo.getDom(el);
30080     this.id = Roo.id(this.el);
30081     this.hidden = false;
30082     
30083     this.addEvents({
30084          /**
30085              * @event render
30086              * Fires when the button is rendered
30087              * @param {Button} this
30088              */
30089         'render': true
30090     });
30091     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30092 };
30093 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30094 //Roo.Toolbar.Item.prototype = {
30095     
30096     /**
30097      * Get this item's HTML Element
30098      * @return {HTMLElement}
30099      */
30100     getEl : function(){
30101        return this.el;  
30102     },
30103
30104     // private
30105     render : function(td){
30106         
30107          this.td = td;
30108         td.appendChild(this.el);
30109         
30110         this.fireEvent('render', this);
30111     },
30112     
30113     /**
30114      * Removes and destroys this item.
30115      */
30116     destroy : function(){
30117         this.td.parentNode.removeChild(this.td);
30118     },
30119     
30120     /**
30121      * Shows this item.
30122      */
30123     show: function(){
30124         this.hidden = false;
30125         this.td.style.display = "";
30126     },
30127     
30128     /**
30129      * Hides this item.
30130      */
30131     hide: function(){
30132         this.hidden = true;
30133         this.td.style.display = "none";
30134     },
30135     
30136     /**
30137      * Convenience function for boolean show/hide.
30138      * @param {Boolean} visible true to show/false to hide
30139      */
30140     setVisible: function(visible){
30141         if(visible) {
30142             this.show();
30143         }else{
30144             this.hide();
30145         }
30146     },
30147     
30148     /**
30149      * Try to focus this item.
30150      */
30151     focus : function(){
30152         Roo.fly(this.el).focus();
30153     },
30154     
30155     /**
30156      * Disables this item.
30157      */
30158     disable : function(){
30159         Roo.fly(this.td).addClass("x-item-disabled");
30160         this.disabled = true;
30161         this.el.disabled = true;
30162     },
30163     
30164     /**
30165      * Enables this item.
30166      */
30167     enable : function(){
30168         Roo.fly(this.td).removeClass("x-item-disabled");
30169         this.disabled = false;
30170         this.el.disabled = false;
30171     }
30172 });
30173
30174
30175 /**
30176  * @class Roo.Toolbar.Separator
30177  * @extends Roo.Toolbar.Item
30178  * A simple toolbar separator class
30179  * @constructor
30180  * Creates a new Separator
30181  */
30182 Roo.Toolbar.Separator = function(cfg){
30183     
30184     var s = document.createElement("span");
30185     s.className = "ytb-sep";
30186     if (cfg) {
30187         cfg.el = s;
30188     }
30189     
30190     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30191 };
30192 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30193     enable:Roo.emptyFn,
30194     disable:Roo.emptyFn,
30195     focus:Roo.emptyFn
30196 });
30197
30198 /**
30199  * @class Roo.Toolbar.Spacer
30200  * @extends Roo.Toolbar.Item
30201  * A simple element that adds extra horizontal space to a toolbar.
30202  * @constructor
30203  * Creates a new Spacer
30204  */
30205 Roo.Toolbar.Spacer = function(cfg){
30206     var s = document.createElement("div");
30207     s.className = "ytb-spacer";
30208     if (cfg) {
30209         cfg.el = s;
30210     }
30211     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30212 };
30213 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30214     enable:Roo.emptyFn,
30215     disable:Roo.emptyFn,
30216     focus:Roo.emptyFn
30217 });
30218
30219 /**
30220  * @class Roo.Toolbar.Fill
30221  * @extends Roo.Toolbar.Spacer
30222  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30223  * @constructor
30224  * Creates a new Spacer
30225  */
30226 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30227     // private
30228     render : function(td){
30229         td.style.width = '100%';
30230         Roo.Toolbar.Fill.superclass.render.call(this, td);
30231     }
30232 });
30233
30234 /**
30235  * @class Roo.Toolbar.TextItem
30236  * @extends Roo.Toolbar.Item
30237  * A simple class that renders text directly into a toolbar.
30238  * @constructor
30239  * Creates a new TextItem
30240  * @param {String} text
30241  */
30242 Roo.Toolbar.TextItem = function(cfg){
30243     var  text = cfg || "";
30244     if (typeof(cfg) == 'object') {
30245         text = cfg.text || "";
30246     }  else {
30247         cfg = null;
30248     }
30249     var s = document.createElement("span");
30250     s.className = "ytb-text";
30251     s.innerHTML = text;
30252     if (cfg) {
30253         cfg.el  = s;
30254     }
30255     
30256     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30257 };
30258 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30259     
30260      
30261     enable:Roo.emptyFn,
30262     disable:Roo.emptyFn,
30263     focus:Roo.emptyFn
30264 });
30265
30266 /**
30267  * @class Roo.Toolbar.Button
30268  * @extends Roo.Button
30269  * A button that renders into a toolbar.
30270  * @constructor
30271  * Creates a new Button
30272  * @param {Object} config A standard {@link Roo.Button} config object
30273  */
30274 Roo.Toolbar.Button = function(config){
30275     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30276 };
30277 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30278     render : function(td){
30279         this.td = td;
30280         Roo.Toolbar.Button.superclass.render.call(this, td);
30281     },
30282     
30283     /**
30284      * Removes and destroys this button
30285      */
30286     destroy : function(){
30287         Roo.Toolbar.Button.superclass.destroy.call(this);
30288         this.td.parentNode.removeChild(this.td);
30289     },
30290     
30291     /**
30292      * Shows this button
30293      */
30294     show: function(){
30295         this.hidden = false;
30296         this.td.style.display = "";
30297     },
30298     
30299     /**
30300      * Hides this button
30301      */
30302     hide: function(){
30303         this.hidden = true;
30304         this.td.style.display = "none";
30305     },
30306
30307     /**
30308      * Disables this item
30309      */
30310     disable : function(){
30311         Roo.fly(this.td).addClass("x-item-disabled");
30312         this.disabled = true;
30313     },
30314
30315     /**
30316      * Enables this item
30317      */
30318     enable : function(){
30319         Roo.fly(this.td).removeClass("x-item-disabled");
30320         this.disabled = false;
30321     }
30322 });
30323 // backwards compat
30324 Roo.ToolbarButton = Roo.Toolbar.Button;
30325
30326 /**
30327  * @class Roo.Toolbar.SplitButton
30328  * @extends Roo.SplitButton
30329  * A menu button that renders into a toolbar.
30330  * @constructor
30331  * Creates a new SplitButton
30332  * @param {Object} config A standard {@link Roo.SplitButton} config object
30333  */
30334 Roo.Toolbar.SplitButton = function(config){
30335     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30336 };
30337 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30338     render : function(td){
30339         this.td = td;
30340         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30341     },
30342     
30343     /**
30344      * Removes and destroys this button
30345      */
30346     destroy : function(){
30347         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30348         this.td.parentNode.removeChild(this.td);
30349     },
30350     
30351     /**
30352      * Shows this button
30353      */
30354     show: function(){
30355         this.hidden = false;
30356         this.td.style.display = "";
30357     },
30358     
30359     /**
30360      * Hides this button
30361      */
30362     hide: function(){
30363         this.hidden = true;
30364         this.td.style.display = "none";
30365     }
30366 });
30367
30368 // backwards compat
30369 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30370  * Based on:
30371  * Ext JS Library 1.1.1
30372  * Copyright(c) 2006-2007, Ext JS, LLC.
30373  *
30374  * Originally Released Under LGPL - original licence link has changed is not relivant.
30375  *
30376  * Fork - LGPL
30377  * <script type="text/javascript">
30378  */
30379  
30380 /**
30381  * @class Roo.PagingToolbar
30382  * @extends Roo.Toolbar
30383  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30384  * @constructor
30385  * Create a new PagingToolbar
30386  * @param {Object} config The config object
30387  */
30388 Roo.PagingToolbar = function(el, ds, config)
30389 {
30390     // old args format still supported... - xtype is prefered..
30391     if (typeof(el) == 'object' && el.xtype) {
30392         // created from xtype...
30393         config = el;
30394         ds = el.dataSource;
30395         el = config.container;
30396     }
30397     var items = [];
30398     if (config.items) {
30399         items = config.items;
30400         config.items = [];
30401     }
30402     
30403     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30404     this.ds = ds;
30405     this.cursor = 0;
30406     this.renderButtons(this.el);
30407     this.bind(ds);
30408     
30409     // supprot items array.
30410    
30411     Roo.each(items, function(e) {
30412         this.add(Roo.factory(e));
30413     },this);
30414     
30415 };
30416
30417 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30418     /**
30419      * @cfg {Roo.data.Store} dataSource
30420      * The underlying data store providing the paged data
30421      */
30422     /**
30423      * @cfg {String/HTMLElement/Element} container
30424      * container The id or element that will contain the toolbar
30425      */
30426     /**
30427      * @cfg {Boolean} displayInfo
30428      * True to display the displayMsg (defaults to false)
30429      */
30430     /**
30431      * @cfg {Number} pageSize
30432      * The number of records to display per page (defaults to 20)
30433      */
30434     pageSize: 20,
30435     /**
30436      * @cfg {String} displayMsg
30437      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30438      */
30439     displayMsg : 'Displaying {0} - {1} of {2}',
30440     /**
30441      * @cfg {String} emptyMsg
30442      * The message to display when no records are found (defaults to "No data to display")
30443      */
30444     emptyMsg : 'No data to display',
30445     /**
30446      * Customizable piece of the default paging text (defaults to "Page")
30447      * @type String
30448      */
30449     beforePageText : "Page",
30450     /**
30451      * Customizable piece of the default paging text (defaults to "of %0")
30452      * @type String
30453      */
30454     afterPageText : "of {0}",
30455     /**
30456      * Customizable piece of the default paging text (defaults to "First Page")
30457      * @type String
30458      */
30459     firstText : "First Page",
30460     /**
30461      * Customizable piece of the default paging text (defaults to "Previous Page")
30462      * @type String
30463      */
30464     prevText : "Previous Page",
30465     /**
30466      * Customizable piece of the default paging text (defaults to "Next Page")
30467      * @type String
30468      */
30469     nextText : "Next Page",
30470     /**
30471      * Customizable piece of the default paging text (defaults to "Last Page")
30472      * @type String
30473      */
30474     lastText : "Last Page",
30475     /**
30476      * Customizable piece of the default paging text (defaults to "Refresh")
30477      * @type String
30478      */
30479     refreshText : "Refresh",
30480
30481     // private
30482     renderButtons : function(el){
30483         Roo.PagingToolbar.superclass.render.call(this, el);
30484         this.first = this.addButton({
30485             tooltip: this.firstText,
30486             cls: "x-btn-icon x-grid-page-first",
30487             disabled: true,
30488             handler: this.onClick.createDelegate(this, ["first"])
30489         });
30490         this.prev = this.addButton({
30491             tooltip: this.prevText,
30492             cls: "x-btn-icon x-grid-page-prev",
30493             disabled: true,
30494             handler: this.onClick.createDelegate(this, ["prev"])
30495         });
30496         //this.addSeparator();
30497         this.add(this.beforePageText);
30498         this.field = Roo.get(this.addDom({
30499            tag: "input",
30500            type: "text",
30501            size: "3",
30502            value: "1",
30503            cls: "x-grid-page-number"
30504         }).el);
30505         this.field.on("keydown", this.onPagingKeydown, this);
30506         this.field.on("focus", function(){this.dom.select();});
30507         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30508         this.field.setHeight(18);
30509         //this.addSeparator();
30510         this.next = this.addButton({
30511             tooltip: this.nextText,
30512             cls: "x-btn-icon x-grid-page-next",
30513             disabled: true,
30514             handler: this.onClick.createDelegate(this, ["next"])
30515         });
30516         this.last = this.addButton({
30517             tooltip: this.lastText,
30518             cls: "x-btn-icon x-grid-page-last",
30519             disabled: true,
30520             handler: this.onClick.createDelegate(this, ["last"])
30521         });
30522         //this.addSeparator();
30523         this.loading = this.addButton({
30524             tooltip: this.refreshText,
30525             cls: "x-btn-icon x-grid-loading",
30526             handler: this.onClick.createDelegate(this, ["refresh"])
30527         });
30528
30529         if(this.displayInfo){
30530             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30531         }
30532     },
30533
30534     // private
30535     updateInfo : function(){
30536         if(this.displayEl){
30537             var count = this.ds.getCount();
30538             var msg = count == 0 ?
30539                 this.emptyMsg :
30540                 String.format(
30541                     this.displayMsg,
30542                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30543                 );
30544             this.displayEl.update(msg);
30545         }
30546     },
30547
30548     // private
30549     onLoad : function(ds, r, o){
30550        this.cursor = o.params ? o.params.start : 0;
30551        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30552
30553        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30554        this.field.dom.value = ap;
30555        this.first.setDisabled(ap == 1);
30556        this.prev.setDisabled(ap == 1);
30557        this.next.setDisabled(ap == ps);
30558        this.last.setDisabled(ap == ps);
30559        this.loading.enable();
30560        this.updateInfo();
30561     },
30562
30563     // private
30564     getPageData : function(){
30565         var total = this.ds.getTotalCount();
30566         return {
30567             total : total,
30568             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30569             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30570         };
30571     },
30572
30573     // private
30574     onLoadError : function(){
30575         this.loading.enable();
30576     },
30577
30578     // private
30579     onPagingKeydown : function(e){
30580         var k = e.getKey();
30581         var d = this.getPageData();
30582         if(k == e.RETURN){
30583             var v = this.field.dom.value, pageNum;
30584             if(!v || isNaN(pageNum = parseInt(v, 10))){
30585                 this.field.dom.value = d.activePage;
30586                 return;
30587             }
30588             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30589             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30590             e.stopEvent();
30591         }
30592         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))
30593         {
30594           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30595           this.field.dom.value = pageNum;
30596           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30597           e.stopEvent();
30598         }
30599         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30600         {
30601           var v = this.field.dom.value, pageNum; 
30602           var increment = (e.shiftKey) ? 10 : 1;
30603           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30604             increment *= -1;
30605           }
30606           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30607             this.field.dom.value = d.activePage;
30608             return;
30609           }
30610           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30611           {
30612             this.field.dom.value = parseInt(v, 10) + increment;
30613             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30614             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30615           }
30616           e.stopEvent();
30617         }
30618     },
30619
30620     // private
30621     beforeLoad : function(){
30622         if(this.loading){
30623             this.loading.disable();
30624         }
30625     },
30626
30627     // private
30628     onClick : function(which){
30629         var ds = this.ds;
30630         switch(which){
30631             case "first":
30632                 ds.load({params:{start: 0, limit: this.pageSize}});
30633             break;
30634             case "prev":
30635                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30636             break;
30637             case "next":
30638                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30639             break;
30640             case "last":
30641                 var total = ds.getTotalCount();
30642                 var extra = total % this.pageSize;
30643                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30644                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30645             break;
30646             case "refresh":
30647                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30648             break;
30649         }
30650     },
30651
30652     /**
30653      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30654      * @param {Roo.data.Store} store The data store to unbind
30655      */
30656     unbind : function(ds){
30657         ds.un("beforeload", this.beforeLoad, this);
30658         ds.un("load", this.onLoad, this);
30659         ds.un("loadexception", this.onLoadError, this);
30660         ds.un("remove", this.updateInfo, this);
30661         ds.un("add", this.updateInfo, this);
30662         this.ds = undefined;
30663     },
30664
30665     /**
30666      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30667      * @param {Roo.data.Store} store The data store to bind
30668      */
30669     bind : function(ds){
30670         ds.on("beforeload", this.beforeLoad, this);
30671         ds.on("load", this.onLoad, this);
30672         ds.on("loadexception", this.onLoadError, this);
30673         ds.on("remove", this.updateInfo, this);
30674         ds.on("add", this.updateInfo, this);
30675         this.ds = ds;
30676     }
30677 });/*
30678  * Based on:
30679  * Ext JS Library 1.1.1
30680  * Copyright(c) 2006-2007, Ext JS, LLC.
30681  *
30682  * Originally Released Under LGPL - original licence link has changed is not relivant.
30683  *
30684  * Fork - LGPL
30685  * <script type="text/javascript">
30686  */
30687
30688 /**
30689  * @class Roo.Resizable
30690  * @extends Roo.util.Observable
30691  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30692  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30693  * 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
30694  * the element will be wrapped for you automatically.</p>
30695  * <p>Here is the list of valid resize handles:</p>
30696  * <pre>
30697 Value   Description
30698 ------  -------------------
30699  'n'     north
30700  's'     south
30701  'e'     east
30702  'w'     west
30703  'nw'    northwest
30704  'sw'    southwest
30705  'se'    southeast
30706  'ne'    northeast
30707  'hd'    horizontal drag
30708  'all'   all
30709 </pre>
30710  * <p>Here's an example showing the creation of a typical Resizable:</p>
30711  * <pre><code>
30712 var resizer = new Roo.Resizable("element-id", {
30713     handles: 'all',
30714     minWidth: 200,
30715     minHeight: 100,
30716     maxWidth: 500,
30717     maxHeight: 400,
30718     pinned: true
30719 });
30720 resizer.on("resize", myHandler);
30721 </code></pre>
30722  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30723  * resizer.east.setDisplayed(false);</p>
30724  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30725  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30726  * resize operation's new size (defaults to [0, 0])
30727  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30728  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30729  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30730  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30731  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30732  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30733  * @cfg {Number} width The width of the element in pixels (defaults to null)
30734  * @cfg {Number} height The height of the element in pixels (defaults to null)
30735  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30736  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30737  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30738  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30739  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30740  * in favor of the handles config option (defaults to false)
30741  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30742  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30743  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30744  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30745  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30746  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30747  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30748  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30749  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30750  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30751  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30752  * @constructor
30753  * Create a new resizable component
30754  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30755  * @param {Object} config configuration options
30756   */
30757 Roo.Resizable = function(el, config)
30758 {
30759     this.el = Roo.get(el);
30760
30761     if(config && config.wrap){
30762         config.resizeChild = this.el;
30763         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30764         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30765         this.el.setStyle("overflow", "hidden");
30766         this.el.setPositioning(config.resizeChild.getPositioning());
30767         config.resizeChild.clearPositioning();
30768         if(!config.width || !config.height){
30769             var csize = config.resizeChild.getSize();
30770             this.el.setSize(csize.width, csize.height);
30771         }
30772         if(config.pinned && !config.adjustments){
30773             config.adjustments = "auto";
30774         }
30775     }
30776
30777     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30778     this.proxy.unselectable();
30779     this.proxy.enableDisplayMode('block');
30780
30781     Roo.apply(this, config);
30782
30783     if(this.pinned){
30784         this.disableTrackOver = true;
30785         this.el.addClass("x-resizable-pinned");
30786     }
30787     // if the element isn't positioned, make it relative
30788     var position = this.el.getStyle("position");
30789     if(position != "absolute" && position != "fixed"){
30790         this.el.setStyle("position", "relative");
30791     }
30792     if(!this.handles){ // no handles passed, must be legacy style
30793         this.handles = 's,e,se';
30794         if(this.multiDirectional){
30795             this.handles += ',n,w';
30796         }
30797     }
30798     if(this.handles == "all"){
30799         this.handles = "n s e w ne nw se sw";
30800     }
30801     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30802     var ps = Roo.Resizable.positions;
30803     for(var i = 0, len = hs.length; i < len; i++){
30804         if(hs[i] && ps[hs[i]]){
30805             var pos = ps[hs[i]];
30806             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30807         }
30808     }
30809     // legacy
30810     this.corner = this.southeast;
30811     
30812     // updateBox = the box can move..
30813     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30814         this.updateBox = true;
30815     }
30816
30817     this.activeHandle = null;
30818
30819     if(this.resizeChild){
30820         if(typeof this.resizeChild == "boolean"){
30821             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30822         }else{
30823             this.resizeChild = Roo.get(this.resizeChild, true);
30824         }
30825     }
30826     
30827     if(this.adjustments == "auto"){
30828         var rc = this.resizeChild;
30829         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30830         if(rc && (hw || hn)){
30831             rc.position("relative");
30832             rc.setLeft(hw ? hw.el.getWidth() : 0);
30833             rc.setTop(hn ? hn.el.getHeight() : 0);
30834         }
30835         this.adjustments = [
30836             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30837             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30838         ];
30839     }
30840
30841     if(this.draggable){
30842         this.dd = this.dynamic ?
30843             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30844         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30845     }
30846
30847     // public events
30848     this.addEvents({
30849         /**
30850          * @event beforeresize
30851          * Fired before resize is allowed. Set enabled to false to cancel resize.
30852          * @param {Roo.Resizable} this
30853          * @param {Roo.EventObject} e The mousedown event
30854          */
30855         "beforeresize" : true,
30856         /**
30857          * @event resizing
30858          * Fired a resizing.
30859          * @param {Roo.Resizable} this
30860          * @param {Number} x The new x position
30861          * @param {Number} y The new y position
30862          * @param {Number} w The new w width
30863          * @param {Number} h The new h hight
30864          * @param {Roo.EventObject} e The mouseup event
30865          */
30866         "resizing" : true,
30867         /**
30868          * @event resize
30869          * Fired after a resize.
30870          * @param {Roo.Resizable} this
30871          * @param {Number} width The new width
30872          * @param {Number} height The new height
30873          * @param {Roo.EventObject} e The mouseup event
30874          */
30875         "resize" : true
30876     });
30877
30878     if(this.width !== null && this.height !== null){
30879         this.resizeTo(this.width, this.height);
30880     }else{
30881         this.updateChildSize();
30882     }
30883     if(Roo.isIE){
30884         this.el.dom.style.zoom = 1;
30885     }
30886     Roo.Resizable.superclass.constructor.call(this);
30887 };
30888
30889 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30890         resizeChild : false,
30891         adjustments : [0, 0],
30892         minWidth : 5,
30893         minHeight : 5,
30894         maxWidth : 10000,
30895         maxHeight : 10000,
30896         enabled : true,
30897         animate : false,
30898         duration : .35,
30899         dynamic : false,
30900         handles : false,
30901         multiDirectional : false,
30902         disableTrackOver : false,
30903         easing : 'easeOutStrong',
30904         widthIncrement : 0,
30905         heightIncrement : 0,
30906         pinned : false,
30907         width : null,
30908         height : null,
30909         preserveRatio : false,
30910         transparent: false,
30911         minX: 0,
30912         minY: 0,
30913         draggable: false,
30914
30915         /**
30916          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30917          */
30918         constrainTo: undefined,
30919         /**
30920          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30921          */
30922         resizeRegion: undefined,
30923
30924
30925     /**
30926      * Perform a manual resize
30927      * @param {Number} width
30928      * @param {Number} height
30929      */
30930     resizeTo : function(width, height){
30931         this.el.setSize(width, height);
30932         this.updateChildSize();
30933         this.fireEvent("resize", this, width, height, null);
30934     },
30935
30936     // private
30937     startSizing : function(e, handle){
30938         this.fireEvent("beforeresize", this, e);
30939         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30940
30941             if(!this.overlay){
30942                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30943                 this.overlay.unselectable();
30944                 this.overlay.enableDisplayMode("block");
30945                 this.overlay.on("mousemove", this.onMouseMove, this);
30946                 this.overlay.on("mouseup", this.onMouseUp, this);
30947             }
30948             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30949
30950             this.resizing = true;
30951             this.startBox = this.el.getBox();
30952             this.startPoint = e.getXY();
30953             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30954                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30955
30956             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30957             this.overlay.show();
30958
30959             if(this.constrainTo) {
30960                 var ct = Roo.get(this.constrainTo);
30961                 this.resizeRegion = ct.getRegion().adjust(
30962                     ct.getFrameWidth('t'),
30963                     ct.getFrameWidth('l'),
30964                     -ct.getFrameWidth('b'),
30965                     -ct.getFrameWidth('r')
30966                 );
30967             }
30968
30969             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30970             this.proxy.show();
30971             this.proxy.setBox(this.startBox);
30972             if(!this.dynamic){
30973                 this.proxy.setStyle('visibility', 'visible');
30974             }
30975         }
30976     },
30977
30978     // private
30979     onMouseDown : function(handle, e){
30980         if(this.enabled){
30981             e.stopEvent();
30982             this.activeHandle = handle;
30983             this.startSizing(e, handle);
30984         }
30985     },
30986
30987     // private
30988     onMouseUp : function(e){
30989         var size = this.resizeElement();
30990         this.resizing = false;
30991         this.handleOut();
30992         this.overlay.hide();
30993         this.proxy.hide();
30994         this.fireEvent("resize", this, size.width, size.height, e);
30995     },
30996
30997     // private
30998     updateChildSize : function(){
30999         
31000         if(this.resizeChild){
31001             var el = this.el;
31002             var child = this.resizeChild;
31003             var adj = this.adjustments;
31004             if(el.dom.offsetWidth){
31005                 var b = el.getSize(true);
31006                 child.setSize(b.width+adj[0], b.height+adj[1]);
31007             }
31008             // Second call here for IE
31009             // The first call enables instant resizing and
31010             // the second call corrects scroll bars if they
31011             // exist
31012             if(Roo.isIE){
31013                 setTimeout(function(){
31014                     if(el.dom.offsetWidth){
31015                         var b = el.getSize(true);
31016                         child.setSize(b.width+adj[0], b.height+adj[1]);
31017                     }
31018                 }, 10);
31019             }
31020         }
31021     },
31022
31023     // private
31024     snap : function(value, inc, min){
31025         if(!inc || !value) {
31026             return value;
31027         }
31028         var newValue = value;
31029         var m = value % inc;
31030         if(m > 0){
31031             if(m > (inc/2)){
31032                 newValue = value + (inc-m);
31033             }else{
31034                 newValue = value - m;
31035             }
31036         }
31037         return Math.max(min, newValue);
31038     },
31039
31040     // private
31041     resizeElement : function(){
31042         var box = this.proxy.getBox();
31043         if(this.updateBox){
31044             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31045         }else{
31046             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31047         }
31048         this.updateChildSize();
31049         if(!this.dynamic){
31050             this.proxy.hide();
31051         }
31052         return box;
31053     },
31054
31055     // private
31056     constrain : function(v, diff, m, mx){
31057         if(v - diff < m){
31058             diff = v - m;
31059         }else if(v - diff > mx){
31060             diff = mx - v;
31061         }
31062         return diff;
31063     },
31064
31065     // private
31066     onMouseMove : function(e){
31067         
31068         if(this.enabled){
31069             try{// try catch so if something goes wrong the user doesn't get hung
31070
31071             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31072                 return;
31073             }
31074
31075             //var curXY = this.startPoint;
31076             var curSize = this.curSize || this.startBox;
31077             var x = this.startBox.x, y = this.startBox.y;
31078             var ox = x, oy = y;
31079             var w = curSize.width, h = curSize.height;
31080             var ow = w, oh = h;
31081             var mw = this.minWidth, mh = this.minHeight;
31082             var mxw = this.maxWidth, mxh = this.maxHeight;
31083             var wi = this.widthIncrement;
31084             var hi = this.heightIncrement;
31085
31086             var eventXY = e.getXY();
31087             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31088             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31089
31090             var pos = this.activeHandle.position;
31091
31092             switch(pos){
31093                 case "east":
31094                     w += diffX;
31095                     w = Math.min(Math.max(mw, w), mxw);
31096                     break;
31097              
31098                 case "south":
31099                     h += diffY;
31100                     h = Math.min(Math.max(mh, h), mxh);
31101                     break;
31102                 case "southeast":
31103                     w += diffX;
31104                     h += diffY;
31105                     w = Math.min(Math.max(mw, w), mxw);
31106                     h = Math.min(Math.max(mh, h), mxh);
31107                     break;
31108                 case "north":
31109                     diffY = this.constrain(h, diffY, mh, mxh);
31110                     y += diffY;
31111                     h -= diffY;
31112                     break;
31113                 case "hdrag":
31114                     
31115                     if (wi) {
31116                         var adiffX = Math.abs(diffX);
31117                         var sub = (adiffX % wi); // how much 
31118                         if (sub > (wi/2)) { // far enough to snap
31119                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31120                         } else {
31121                             // remove difference.. 
31122                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31123                         }
31124                     }
31125                     x += diffX;
31126                     x = Math.max(this.minX, x);
31127                     break;
31128                 case "west":
31129                     diffX = this.constrain(w, diffX, mw, mxw);
31130                     x += diffX;
31131                     w -= diffX;
31132                     break;
31133                 case "northeast":
31134                     w += diffX;
31135                     w = Math.min(Math.max(mw, w), mxw);
31136                     diffY = this.constrain(h, diffY, mh, mxh);
31137                     y += diffY;
31138                     h -= diffY;
31139                     break;
31140                 case "northwest":
31141                     diffX = this.constrain(w, diffX, mw, mxw);
31142                     diffY = this.constrain(h, diffY, mh, mxh);
31143                     y += diffY;
31144                     h -= diffY;
31145                     x += diffX;
31146                     w -= diffX;
31147                     break;
31148                case "southwest":
31149                     diffX = this.constrain(w, diffX, mw, mxw);
31150                     h += diffY;
31151                     h = Math.min(Math.max(mh, h), mxh);
31152                     x += diffX;
31153                     w -= diffX;
31154                     break;
31155             }
31156
31157             var sw = this.snap(w, wi, mw);
31158             var sh = this.snap(h, hi, mh);
31159             if(sw != w || sh != h){
31160                 switch(pos){
31161                     case "northeast":
31162                         y -= sh - h;
31163                     break;
31164                     case "north":
31165                         y -= sh - h;
31166                         break;
31167                     case "southwest":
31168                         x -= sw - w;
31169                     break;
31170                     case "west":
31171                         x -= sw - w;
31172                         break;
31173                     case "northwest":
31174                         x -= sw - w;
31175                         y -= sh - h;
31176                     break;
31177                 }
31178                 w = sw;
31179                 h = sh;
31180             }
31181
31182             if(this.preserveRatio){
31183                 switch(pos){
31184                     case "southeast":
31185                     case "east":
31186                         h = oh * (w/ow);
31187                         h = Math.min(Math.max(mh, h), mxh);
31188                         w = ow * (h/oh);
31189                        break;
31190                     case "south":
31191                         w = ow * (h/oh);
31192                         w = Math.min(Math.max(mw, w), mxw);
31193                         h = oh * (w/ow);
31194                         break;
31195                     case "northeast":
31196                         w = ow * (h/oh);
31197                         w = Math.min(Math.max(mw, w), mxw);
31198                         h = oh * (w/ow);
31199                     break;
31200                     case "north":
31201                         var tw = w;
31202                         w = ow * (h/oh);
31203                         w = Math.min(Math.max(mw, w), mxw);
31204                         h = oh * (w/ow);
31205                         x += (tw - w) / 2;
31206                         break;
31207                     case "southwest":
31208                         h = oh * (w/ow);
31209                         h = Math.min(Math.max(mh, h), mxh);
31210                         var tw = w;
31211                         w = ow * (h/oh);
31212                         x += tw - w;
31213                         break;
31214                     case "west":
31215                         var th = h;
31216                         h = oh * (w/ow);
31217                         h = Math.min(Math.max(mh, h), mxh);
31218                         y += (th - h) / 2;
31219                         var tw = w;
31220                         w = ow * (h/oh);
31221                         x += tw - w;
31222                        break;
31223                     case "northwest":
31224                         var tw = w;
31225                         var th = h;
31226                         h = oh * (w/ow);
31227                         h = Math.min(Math.max(mh, h), mxh);
31228                         w = ow * (h/oh);
31229                         y += th - h;
31230                         x += tw - w;
31231                        break;
31232
31233                 }
31234             }
31235             if (pos == 'hdrag') {
31236                 w = ow;
31237             }
31238             this.proxy.setBounds(x, y, w, h);
31239             if(this.dynamic){
31240                 this.resizeElement();
31241             }
31242             }catch(e){}
31243         }
31244         this.fireEvent("resizing", this, x, y, w, h, e);
31245     },
31246
31247     // private
31248     handleOver : function(){
31249         if(this.enabled){
31250             this.el.addClass("x-resizable-over");
31251         }
31252     },
31253
31254     // private
31255     handleOut : function(){
31256         if(!this.resizing){
31257             this.el.removeClass("x-resizable-over");
31258         }
31259     },
31260
31261     /**
31262      * Returns the element this component is bound to.
31263      * @return {Roo.Element}
31264      */
31265     getEl : function(){
31266         return this.el;
31267     },
31268
31269     /**
31270      * Returns the resizeChild element (or null).
31271      * @return {Roo.Element}
31272      */
31273     getResizeChild : function(){
31274         return this.resizeChild;
31275     },
31276     groupHandler : function()
31277     {
31278         
31279     },
31280     /**
31281      * Destroys this resizable. If the element was wrapped and
31282      * removeEl is not true then the element remains.
31283      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31284      */
31285     destroy : function(removeEl){
31286         this.proxy.remove();
31287         if(this.overlay){
31288             this.overlay.removeAllListeners();
31289             this.overlay.remove();
31290         }
31291         var ps = Roo.Resizable.positions;
31292         for(var k in ps){
31293             if(typeof ps[k] != "function" && this[ps[k]]){
31294                 var h = this[ps[k]];
31295                 h.el.removeAllListeners();
31296                 h.el.remove();
31297             }
31298         }
31299         if(removeEl){
31300             this.el.update("");
31301             this.el.remove();
31302         }
31303     }
31304 });
31305
31306 // private
31307 // hash to map config positions to true positions
31308 Roo.Resizable.positions = {
31309     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31310     hd: "hdrag"
31311 };
31312
31313 // private
31314 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31315     if(!this.tpl){
31316         // only initialize the template if resizable is used
31317         var tpl = Roo.DomHelper.createTemplate(
31318             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31319         );
31320         tpl.compile();
31321         Roo.Resizable.Handle.prototype.tpl = tpl;
31322     }
31323     this.position = pos;
31324     this.rz = rz;
31325     // show north drag fro topdra
31326     var handlepos = pos == 'hdrag' ? 'north' : pos;
31327     
31328     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31329     if (pos == 'hdrag') {
31330         this.el.setStyle('cursor', 'pointer');
31331     }
31332     this.el.unselectable();
31333     if(transparent){
31334         this.el.setOpacity(0);
31335     }
31336     this.el.on("mousedown", this.onMouseDown, this);
31337     if(!disableTrackOver){
31338         this.el.on("mouseover", this.onMouseOver, this);
31339         this.el.on("mouseout", this.onMouseOut, this);
31340     }
31341 };
31342
31343 // private
31344 Roo.Resizable.Handle.prototype = {
31345     afterResize : function(rz){
31346         Roo.log('after?');
31347         // do nothing
31348     },
31349     // private
31350     onMouseDown : function(e){
31351         this.rz.onMouseDown(this, e);
31352     },
31353     // private
31354     onMouseOver : function(e){
31355         this.rz.handleOver(this, e);
31356     },
31357     // private
31358     onMouseOut : function(e){
31359         this.rz.handleOut(this, e);
31360     }
31361 };/*
31362  * Based on:
31363  * Ext JS Library 1.1.1
31364  * Copyright(c) 2006-2007, Ext JS, LLC.
31365  *
31366  * Originally Released Under LGPL - original licence link has changed is not relivant.
31367  *
31368  * Fork - LGPL
31369  * <script type="text/javascript">
31370  */
31371
31372 /**
31373  * @class Roo.Editor
31374  * @extends Roo.Component
31375  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31376  * @constructor
31377  * Create a new Editor
31378  * @param {Roo.form.Field} field The Field object (or descendant)
31379  * @param {Object} config The config object
31380  */
31381 Roo.Editor = function(field, config){
31382     Roo.Editor.superclass.constructor.call(this, config);
31383     this.field = field;
31384     this.addEvents({
31385         /**
31386              * @event beforestartedit
31387              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31388              * false from the handler of this event.
31389              * @param {Editor} this
31390              * @param {Roo.Element} boundEl The underlying element bound to this editor
31391              * @param {Mixed} value The field value being set
31392              */
31393         "beforestartedit" : true,
31394         /**
31395              * @event startedit
31396              * Fires when this editor is displayed
31397              * @param {Roo.Element} boundEl The underlying element bound to this editor
31398              * @param {Mixed} value The starting field value
31399              */
31400         "startedit" : true,
31401         /**
31402              * @event beforecomplete
31403              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31404              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31405              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31406              * event will not fire since no edit actually occurred.
31407              * @param {Editor} this
31408              * @param {Mixed} value The current field value
31409              * @param {Mixed} startValue The original field value
31410              */
31411         "beforecomplete" : true,
31412         /**
31413              * @event complete
31414              * Fires after editing is complete and any changed value has been written to the underlying field.
31415              * @param {Editor} this
31416              * @param {Mixed} value The current field value
31417              * @param {Mixed} startValue The original field value
31418              */
31419         "complete" : true,
31420         /**
31421          * @event specialkey
31422          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31423          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31424          * @param {Roo.form.Field} this
31425          * @param {Roo.EventObject} e The event object
31426          */
31427         "specialkey" : true
31428     });
31429 };
31430
31431 Roo.extend(Roo.Editor, Roo.Component, {
31432     /**
31433      * @cfg {Boolean/String} autosize
31434      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31435      * or "height" to adopt the height only (defaults to false)
31436      */
31437     /**
31438      * @cfg {Boolean} revertInvalid
31439      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31440      * validation fails (defaults to true)
31441      */
31442     /**
31443      * @cfg {Boolean} ignoreNoChange
31444      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31445      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31446      * will never be ignored.
31447      */
31448     /**
31449      * @cfg {Boolean} hideEl
31450      * False to keep the bound element visible while the editor is displayed (defaults to true)
31451      */
31452     /**
31453      * @cfg {Mixed} value
31454      * The data value of the underlying field (defaults to "")
31455      */
31456     value : "",
31457     /**
31458      * @cfg {String} alignment
31459      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31460      */
31461     alignment: "c-c?",
31462     /**
31463      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31464      * for bottom-right shadow (defaults to "frame")
31465      */
31466     shadow : "frame",
31467     /**
31468      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31469      */
31470     constrain : false,
31471     /**
31472      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31473      */
31474     completeOnEnter : false,
31475     /**
31476      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31477      */
31478     cancelOnEsc : false,
31479     /**
31480      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31481      */
31482     updateEl : false,
31483
31484     // private
31485     onRender : function(ct, position){
31486         this.el = new Roo.Layer({
31487             shadow: this.shadow,
31488             cls: "x-editor",
31489             parentEl : ct,
31490             shim : this.shim,
31491             shadowOffset:4,
31492             id: this.id,
31493             constrain: this.constrain
31494         });
31495         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31496         if(this.field.msgTarget != 'title'){
31497             this.field.msgTarget = 'qtip';
31498         }
31499         this.field.render(this.el);
31500         if(Roo.isGecko){
31501             this.field.el.dom.setAttribute('autocomplete', 'off');
31502         }
31503         this.field.on("specialkey", this.onSpecialKey, this);
31504         if(this.swallowKeys){
31505             this.field.el.swallowEvent(['keydown','keypress']);
31506         }
31507         this.field.show();
31508         this.field.on("blur", this.onBlur, this);
31509         if(this.field.grow){
31510             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31511         }
31512     },
31513
31514     onSpecialKey : function(field, e)
31515     {
31516         //Roo.log('editor onSpecialKey');
31517         if(this.completeOnEnter && e.getKey() == e.ENTER){
31518             e.stopEvent();
31519             this.completeEdit();
31520             return;
31521         }
31522         // do not fire special key otherwise it might hide close the editor...
31523         if(e.getKey() == e.ENTER){    
31524             return;
31525         }
31526         if(this.cancelOnEsc && e.getKey() == e.ESC){
31527             this.cancelEdit();
31528             return;
31529         } 
31530         this.fireEvent('specialkey', field, e);
31531     
31532     },
31533
31534     /**
31535      * Starts the editing process and shows the editor.
31536      * @param {String/HTMLElement/Element} el The element to edit
31537      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31538       * to the innerHTML of el.
31539      */
31540     startEdit : function(el, value){
31541         if(this.editing){
31542             this.completeEdit();
31543         }
31544         this.boundEl = Roo.get(el);
31545         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31546         if(!this.rendered){
31547             this.render(this.parentEl || document.body);
31548         }
31549         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31550             return;
31551         }
31552         this.startValue = v;
31553         this.field.setValue(v);
31554         if(this.autoSize){
31555             var sz = this.boundEl.getSize();
31556             switch(this.autoSize){
31557                 case "width":
31558                 this.setSize(sz.width,  "");
31559                 break;
31560                 case "height":
31561                 this.setSize("",  sz.height);
31562                 break;
31563                 default:
31564                 this.setSize(sz.width,  sz.height);
31565             }
31566         }
31567         this.el.alignTo(this.boundEl, this.alignment);
31568         this.editing = true;
31569         if(Roo.QuickTips){
31570             Roo.QuickTips.disable();
31571         }
31572         this.show();
31573     },
31574
31575     /**
31576      * Sets the height and width of this editor.
31577      * @param {Number} width The new width
31578      * @param {Number} height The new height
31579      */
31580     setSize : function(w, h){
31581         this.field.setSize(w, h);
31582         if(this.el){
31583             this.el.sync();
31584         }
31585     },
31586
31587     /**
31588      * Realigns the editor to the bound field based on the current alignment config value.
31589      */
31590     realign : function(){
31591         this.el.alignTo(this.boundEl, this.alignment);
31592     },
31593
31594     /**
31595      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31596      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31597      */
31598     completeEdit : function(remainVisible){
31599         if(!this.editing){
31600             return;
31601         }
31602         var v = this.getValue();
31603         if(this.revertInvalid !== false && !this.field.isValid()){
31604             v = this.startValue;
31605             this.cancelEdit(true);
31606         }
31607         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31608             this.editing = false;
31609             this.hide();
31610             return;
31611         }
31612         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31613             this.editing = false;
31614             if(this.updateEl && this.boundEl){
31615                 this.boundEl.update(v);
31616             }
31617             if(remainVisible !== true){
31618                 this.hide();
31619             }
31620             this.fireEvent("complete", this, v, this.startValue);
31621         }
31622     },
31623
31624     // private
31625     onShow : function(){
31626         this.el.show();
31627         if(this.hideEl !== false){
31628             this.boundEl.hide();
31629         }
31630         this.field.show();
31631         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31632             this.fixIEFocus = true;
31633             this.deferredFocus.defer(50, this);
31634         }else{
31635             this.field.focus();
31636         }
31637         this.fireEvent("startedit", this.boundEl, this.startValue);
31638     },
31639
31640     deferredFocus : function(){
31641         if(this.editing){
31642             this.field.focus();
31643         }
31644     },
31645
31646     /**
31647      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31648      * reverted to the original starting value.
31649      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31650      * cancel (defaults to false)
31651      */
31652     cancelEdit : function(remainVisible){
31653         if(this.editing){
31654             this.setValue(this.startValue);
31655             if(remainVisible !== true){
31656                 this.hide();
31657             }
31658         }
31659     },
31660
31661     // private
31662     onBlur : function(){
31663         if(this.allowBlur !== true && this.editing){
31664             this.completeEdit();
31665         }
31666     },
31667
31668     // private
31669     onHide : function(){
31670         if(this.editing){
31671             this.completeEdit();
31672             return;
31673         }
31674         this.field.blur();
31675         if(this.field.collapse){
31676             this.field.collapse();
31677         }
31678         this.el.hide();
31679         if(this.hideEl !== false){
31680             this.boundEl.show();
31681         }
31682         if(Roo.QuickTips){
31683             Roo.QuickTips.enable();
31684         }
31685     },
31686
31687     /**
31688      * Sets the data value of the editor
31689      * @param {Mixed} value Any valid value supported by the underlying field
31690      */
31691     setValue : function(v){
31692         this.field.setValue(v);
31693     },
31694
31695     /**
31696      * Gets the data value of the editor
31697      * @return {Mixed} The data value
31698      */
31699     getValue : function(){
31700         return this.field.getValue();
31701     }
31702 });/*
31703  * Based on:
31704  * Ext JS Library 1.1.1
31705  * Copyright(c) 2006-2007, Ext JS, LLC.
31706  *
31707  * Originally Released Under LGPL - original licence link has changed is not relivant.
31708  *
31709  * Fork - LGPL
31710  * <script type="text/javascript">
31711  */
31712  
31713 /**
31714  * @class Roo.BasicDialog
31715  * @extends Roo.util.Observable
31716  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31717  * <pre><code>
31718 var dlg = new Roo.BasicDialog("my-dlg", {
31719     height: 200,
31720     width: 300,
31721     minHeight: 100,
31722     minWidth: 150,
31723     modal: true,
31724     proxyDrag: true,
31725     shadow: true
31726 });
31727 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31728 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31729 dlg.addButton('Cancel', dlg.hide, dlg);
31730 dlg.show();
31731 </code></pre>
31732   <b>A Dialog should always be a direct child of the body element.</b>
31733  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31734  * @cfg {String} title Default text to display in the title bar (defaults to null)
31735  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31736  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31737  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31738  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31739  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31740  * (defaults to null with no animation)
31741  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31742  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31743  * property for valid values (defaults to 'all')
31744  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31745  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31746  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31747  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31748  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31749  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31750  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31751  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31752  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31753  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31754  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31755  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31756  * draggable = true (defaults to false)
31757  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31758  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31759  * shadow (defaults to false)
31760  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31761  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31762  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31763  * @cfg {Array} buttons Array of buttons
31764  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31765  * @constructor
31766  * Create a new BasicDialog.
31767  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31768  * @param {Object} config Configuration options
31769  */
31770 Roo.BasicDialog = function(el, config){
31771     this.el = Roo.get(el);
31772     var dh = Roo.DomHelper;
31773     if(!this.el && config && config.autoCreate){
31774         if(typeof config.autoCreate == "object"){
31775             if(!config.autoCreate.id){
31776                 config.autoCreate.id = el;
31777             }
31778             this.el = dh.append(document.body,
31779                         config.autoCreate, true);
31780         }else{
31781             this.el = dh.append(document.body,
31782                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31783         }
31784     }
31785     el = this.el;
31786     el.setDisplayed(true);
31787     el.hide = this.hideAction;
31788     this.id = el.id;
31789     el.addClass("x-dlg");
31790
31791     Roo.apply(this, config);
31792
31793     this.proxy = el.createProxy("x-dlg-proxy");
31794     this.proxy.hide = this.hideAction;
31795     this.proxy.setOpacity(.5);
31796     this.proxy.hide();
31797
31798     if(config.width){
31799         el.setWidth(config.width);
31800     }
31801     if(config.height){
31802         el.setHeight(config.height);
31803     }
31804     this.size = el.getSize();
31805     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31806         this.xy = [config.x,config.y];
31807     }else{
31808         this.xy = el.getCenterXY(true);
31809     }
31810     /** The header element @type Roo.Element */
31811     this.header = el.child("> .x-dlg-hd");
31812     /** The body element @type Roo.Element */
31813     this.body = el.child("> .x-dlg-bd");
31814     /** The footer element @type Roo.Element */
31815     this.footer = el.child("> .x-dlg-ft");
31816
31817     if(!this.header){
31818         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31819     }
31820     if(!this.body){
31821         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31822     }
31823
31824     this.header.unselectable();
31825     if(this.title){
31826         this.header.update(this.title);
31827     }
31828     // this element allows the dialog to be focused for keyboard event
31829     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31830     this.focusEl.swallowEvent("click", true);
31831
31832     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31833
31834     // wrap the body and footer for special rendering
31835     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31836     if(this.footer){
31837         this.bwrap.dom.appendChild(this.footer.dom);
31838     }
31839
31840     this.bg = this.el.createChild({
31841         tag: "div", cls:"x-dlg-bg",
31842         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31843     });
31844     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31845
31846
31847     if(this.autoScroll !== false && !this.autoTabs){
31848         this.body.setStyle("overflow", "auto");
31849     }
31850
31851     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31852
31853     if(this.closable !== false){
31854         this.el.addClass("x-dlg-closable");
31855         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31856         this.close.on("click", this.closeClick, this);
31857         this.close.addClassOnOver("x-dlg-close-over");
31858     }
31859     if(this.collapsible !== false){
31860         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31861         this.collapseBtn.on("click", this.collapseClick, this);
31862         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31863         this.header.on("dblclick", this.collapseClick, this);
31864     }
31865     if(this.resizable !== false){
31866         this.el.addClass("x-dlg-resizable");
31867         this.resizer = new Roo.Resizable(el, {
31868             minWidth: this.minWidth || 80,
31869             minHeight:this.minHeight || 80,
31870             handles: this.resizeHandles || "all",
31871             pinned: true
31872         });
31873         this.resizer.on("beforeresize", this.beforeResize, this);
31874         this.resizer.on("resize", this.onResize, this);
31875     }
31876     if(this.draggable !== false){
31877         el.addClass("x-dlg-draggable");
31878         if (!this.proxyDrag) {
31879             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31880         }
31881         else {
31882             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31883         }
31884         dd.setHandleElId(this.header.id);
31885         dd.endDrag = this.endMove.createDelegate(this);
31886         dd.startDrag = this.startMove.createDelegate(this);
31887         dd.onDrag = this.onDrag.createDelegate(this);
31888         dd.scroll = false;
31889         this.dd = dd;
31890     }
31891     if(this.modal){
31892         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31893         this.mask.enableDisplayMode("block");
31894         this.mask.hide();
31895         this.el.addClass("x-dlg-modal");
31896     }
31897     if(this.shadow){
31898         this.shadow = new Roo.Shadow({
31899             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31900             offset : this.shadowOffset
31901         });
31902     }else{
31903         this.shadowOffset = 0;
31904     }
31905     if(Roo.useShims && this.shim !== false){
31906         this.shim = this.el.createShim();
31907         this.shim.hide = this.hideAction;
31908         this.shim.hide();
31909     }else{
31910         this.shim = false;
31911     }
31912     if(this.autoTabs){
31913         this.initTabs();
31914     }
31915     if (this.buttons) { 
31916         var bts= this.buttons;
31917         this.buttons = [];
31918         Roo.each(bts, function(b) {
31919             this.addButton(b);
31920         }, this);
31921     }
31922     
31923     
31924     this.addEvents({
31925         /**
31926          * @event keydown
31927          * Fires when a key is pressed
31928          * @param {Roo.BasicDialog} this
31929          * @param {Roo.EventObject} e
31930          */
31931         "keydown" : true,
31932         /**
31933          * @event move
31934          * Fires when this dialog is moved by the user.
31935          * @param {Roo.BasicDialog} this
31936          * @param {Number} x The new page X
31937          * @param {Number} y The new page Y
31938          */
31939         "move" : true,
31940         /**
31941          * @event resize
31942          * Fires when this dialog is resized by the user.
31943          * @param {Roo.BasicDialog} this
31944          * @param {Number} width The new width
31945          * @param {Number} height The new height
31946          */
31947         "resize" : true,
31948         /**
31949          * @event beforehide
31950          * Fires before this dialog is hidden.
31951          * @param {Roo.BasicDialog} this
31952          */
31953         "beforehide" : true,
31954         /**
31955          * @event hide
31956          * Fires when this dialog is hidden.
31957          * @param {Roo.BasicDialog} this
31958          */
31959         "hide" : true,
31960         /**
31961          * @event beforeshow
31962          * Fires before this dialog is shown.
31963          * @param {Roo.BasicDialog} this
31964          */
31965         "beforeshow" : true,
31966         /**
31967          * @event show
31968          * Fires when this dialog is shown.
31969          * @param {Roo.BasicDialog} this
31970          */
31971         "show" : true
31972     });
31973     el.on("keydown", this.onKeyDown, this);
31974     el.on("mousedown", this.toFront, this);
31975     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31976     this.el.hide();
31977     Roo.DialogManager.register(this);
31978     Roo.BasicDialog.superclass.constructor.call(this);
31979 };
31980
31981 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31982     shadowOffset: Roo.isIE ? 6 : 5,
31983     minHeight: 80,
31984     minWidth: 200,
31985     minButtonWidth: 75,
31986     defaultButton: null,
31987     buttonAlign: "right",
31988     tabTag: 'div',
31989     firstShow: true,
31990
31991     /**
31992      * Sets the dialog title text
31993      * @param {String} text The title text to display
31994      * @return {Roo.BasicDialog} this
31995      */
31996     setTitle : function(text){
31997         this.header.update(text);
31998         return this;
31999     },
32000
32001     // private
32002     closeClick : function(){
32003         this.hide();
32004     },
32005
32006     // private
32007     collapseClick : function(){
32008         this[this.collapsed ? "expand" : "collapse"]();
32009     },
32010
32011     /**
32012      * Collapses the dialog to its minimized state (only the title bar is visible).
32013      * Equivalent to the user clicking the collapse dialog button.
32014      */
32015     collapse : function(){
32016         if(!this.collapsed){
32017             this.collapsed = true;
32018             this.el.addClass("x-dlg-collapsed");
32019             this.restoreHeight = this.el.getHeight();
32020             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32021         }
32022     },
32023
32024     /**
32025      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32026      * clicking the expand dialog button.
32027      */
32028     expand : function(){
32029         if(this.collapsed){
32030             this.collapsed = false;
32031             this.el.removeClass("x-dlg-collapsed");
32032             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32033         }
32034     },
32035
32036     /**
32037      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32038      * @return {Roo.TabPanel} The tabs component
32039      */
32040     initTabs : function(){
32041         var tabs = this.getTabs();
32042         while(tabs.getTab(0)){
32043             tabs.removeTab(0);
32044         }
32045         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32046             var dom = el.dom;
32047             tabs.addTab(Roo.id(dom), dom.title);
32048             dom.title = "";
32049         });
32050         tabs.activate(0);
32051         return tabs;
32052     },
32053
32054     // private
32055     beforeResize : function(){
32056         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32057     },
32058
32059     // private
32060     onResize : function(){
32061         this.refreshSize();
32062         this.syncBodyHeight();
32063         this.adjustAssets();
32064         this.focus();
32065         this.fireEvent("resize", this, this.size.width, this.size.height);
32066     },
32067
32068     // private
32069     onKeyDown : function(e){
32070         if(this.isVisible()){
32071             this.fireEvent("keydown", this, e);
32072         }
32073     },
32074
32075     /**
32076      * Resizes the dialog.
32077      * @param {Number} width
32078      * @param {Number} height
32079      * @return {Roo.BasicDialog} this
32080      */
32081     resizeTo : function(width, height){
32082         this.el.setSize(width, height);
32083         this.size = {width: width, height: height};
32084         this.syncBodyHeight();
32085         if(this.fixedcenter){
32086             this.center();
32087         }
32088         if(this.isVisible()){
32089             this.constrainXY();
32090             this.adjustAssets();
32091         }
32092         this.fireEvent("resize", this, width, height);
32093         return this;
32094     },
32095
32096
32097     /**
32098      * Resizes the dialog to fit the specified content size.
32099      * @param {Number} width
32100      * @param {Number} height
32101      * @return {Roo.BasicDialog} this
32102      */
32103     setContentSize : function(w, h){
32104         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32105         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32106         //if(!this.el.isBorderBox()){
32107             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32108             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32109         //}
32110         if(this.tabs){
32111             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32112             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32113         }
32114         this.resizeTo(w, h);
32115         return this;
32116     },
32117
32118     /**
32119      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32120      * executed in response to a particular key being pressed while the dialog is active.
32121      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32122      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32123      * @param {Function} fn The function to call
32124      * @param {Object} scope (optional) The scope of the function
32125      * @return {Roo.BasicDialog} this
32126      */
32127     addKeyListener : function(key, fn, scope){
32128         var keyCode, shift, ctrl, alt;
32129         if(typeof key == "object" && !(key instanceof Array)){
32130             keyCode = key["key"];
32131             shift = key["shift"];
32132             ctrl = key["ctrl"];
32133             alt = key["alt"];
32134         }else{
32135             keyCode = key;
32136         }
32137         var handler = function(dlg, e){
32138             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32139                 var k = e.getKey();
32140                 if(keyCode instanceof Array){
32141                     for(var i = 0, len = keyCode.length; i < len; i++){
32142                         if(keyCode[i] == k){
32143                           fn.call(scope || window, dlg, k, e);
32144                           return;
32145                         }
32146                     }
32147                 }else{
32148                     if(k == keyCode){
32149                         fn.call(scope || window, dlg, k, e);
32150                     }
32151                 }
32152             }
32153         };
32154         this.on("keydown", handler);
32155         return this;
32156     },
32157
32158     /**
32159      * Returns the TabPanel component (creates it if it doesn't exist).
32160      * Note: If you wish to simply check for the existence of tabs without creating them,
32161      * check for a null 'tabs' property.
32162      * @return {Roo.TabPanel} The tabs component
32163      */
32164     getTabs : function(){
32165         if(!this.tabs){
32166             this.el.addClass("x-dlg-auto-tabs");
32167             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32168             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32169         }
32170         return this.tabs;
32171     },
32172
32173     /**
32174      * Adds a button to the footer section of the dialog.
32175      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32176      * object or a valid Roo.DomHelper element config
32177      * @param {Function} handler The function called when the button is clicked
32178      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32179      * @return {Roo.Button} The new button
32180      */
32181     addButton : function(config, handler, scope){
32182         var dh = Roo.DomHelper;
32183         if(!this.footer){
32184             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32185         }
32186         if(!this.btnContainer){
32187             var tb = this.footer.createChild({
32188
32189                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32190                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32191             }, null, true);
32192             this.btnContainer = tb.firstChild.firstChild.firstChild;
32193         }
32194         var bconfig = {
32195             handler: handler,
32196             scope: scope,
32197             minWidth: this.minButtonWidth,
32198             hideParent:true
32199         };
32200         if(typeof config == "string"){
32201             bconfig.text = config;
32202         }else{
32203             if(config.tag){
32204                 bconfig.dhconfig = config;
32205             }else{
32206                 Roo.apply(bconfig, config);
32207             }
32208         }
32209         var fc = false;
32210         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32211             bconfig.position = Math.max(0, bconfig.position);
32212             fc = this.btnContainer.childNodes[bconfig.position];
32213         }
32214          
32215         var btn = new Roo.Button(
32216             fc ? 
32217                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32218                 : this.btnContainer.appendChild(document.createElement("td")),
32219             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32220             bconfig
32221         );
32222         this.syncBodyHeight();
32223         if(!this.buttons){
32224             /**
32225              * Array of all the buttons that have been added to this dialog via addButton
32226              * @type Array
32227              */
32228             this.buttons = [];
32229         }
32230         this.buttons.push(btn);
32231         return btn;
32232     },
32233
32234     /**
32235      * Sets the default button to be focused when the dialog is displayed.
32236      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32237      * @return {Roo.BasicDialog} this
32238      */
32239     setDefaultButton : function(btn){
32240         this.defaultButton = btn;
32241         return this;
32242     },
32243
32244     // private
32245     getHeaderFooterHeight : function(safe){
32246         var height = 0;
32247         if(this.header){
32248            height += this.header.getHeight();
32249         }
32250         if(this.footer){
32251            var fm = this.footer.getMargins();
32252             height += (this.footer.getHeight()+fm.top+fm.bottom);
32253         }
32254         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32255         height += this.centerBg.getPadding("tb");
32256         return height;
32257     },
32258
32259     // private
32260     syncBodyHeight : function()
32261     {
32262         var bd = this.body, // the text
32263             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32264             bw = this.bwrap;
32265         var height = this.size.height - this.getHeaderFooterHeight(false);
32266         bd.setHeight(height-bd.getMargins("tb"));
32267         var hh = this.header.getHeight();
32268         var h = this.size.height-hh;
32269         cb.setHeight(h);
32270         
32271         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32272         bw.setHeight(h-cb.getPadding("tb"));
32273         
32274         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32275         bd.setWidth(bw.getWidth(true));
32276         if(this.tabs){
32277             this.tabs.syncHeight();
32278             if(Roo.isIE){
32279                 this.tabs.el.repaint();
32280             }
32281         }
32282     },
32283
32284     /**
32285      * Restores the previous state of the dialog if Roo.state is configured.
32286      * @return {Roo.BasicDialog} this
32287      */
32288     restoreState : function(){
32289         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32290         if(box && box.width){
32291             this.xy = [box.x, box.y];
32292             this.resizeTo(box.width, box.height);
32293         }
32294         return this;
32295     },
32296
32297     // private
32298     beforeShow : function(){
32299         this.expand();
32300         if(this.fixedcenter){
32301             this.xy = this.el.getCenterXY(true);
32302         }
32303         if(this.modal){
32304             Roo.get(document.body).addClass("x-body-masked");
32305             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32306             this.mask.show();
32307         }
32308         this.constrainXY();
32309     },
32310
32311     // private
32312     animShow : function(){
32313         var b = Roo.get(this.animateTarget).getBox();
32314         this.proxy.setSize(b.width, b.height);
32315         this.proxy.setLocation(b.x, b.y);
32316         this.proxy.show();
32317         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32318                     true, .35, this.showEl.createDelegate(this));
32319     },
32320
32321     /**
32322      * Shows the dialog.
32323      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32324      * @return {Roo.BasicDialog} this
32325      */
32326     show : function(animateTarget){
32327         if (this.fireEvent("beforeshow", this) === false){
32328             return;
32329         }
32330         if(this.syncHeightBeforeShow){
32331             this.syncBodyHeight();
32332         }else if(this.firstShow){
32333             this.firstShow = false;
32334             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32335         }
32336         this.animateTarget = animateTarget || this.animateTarget;
32337         if(!this.el.isVisible()){
32338             this.beforeShow();
32339             if(this.animateTarget && Roo.get(this.animateTarget)){
32340                 this.animShow();
32341             }else{
32342                 this.showEl();
32343             }
32344         }
32345         return this;
32346     },
32347
32348     // private
32349     showEl : function(){
32350         this.proxy.hide();
32351         this.el.setXY(this.xy);
32352         this.el.show();
32353         this.adjustAssets(true);
32354         this.toFront();
32355         this.focus();
32356         // IE peekaboo bug - fix found by Dave Fenwick
32357         if(Roo.isIE){
32358             this.el.repaint();
32359         }
32360         this.fireEvent("show", this);
32361     },
32362
32363     /**
32364      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32365      * dialog itself will receive focus.
32366      */
32367     focus : function(){
32368         if(this.defaultButton){
32369             this.defaultButton.focus();
32370         }else{
32371             this.focusEl.focus();
32372         }
32373     },
32374
32375     // private
32376     constrainXY : function(){
32377         if(this.constraintoviewport !== false){
32378             if(!this.viewSize){
32379                 if(this.container){
32380                     var s = this.container.getSize();
32381                     this.viewSize = [s.width, s.height];
32382                 }else{
32383                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32384                 }
32385             }
32386             var s = Roo.get(this.container||document).getScroll();
32387
32388             var x = this.xy[0], y = this.xy[1];
32389             var w = this.size.width, h = this.size.height;
32390             var vw = this.viewSize[0], vh = this.viewSize[1];
32391             // only move it if it needs it
32392             var moved = false;
32393             // first validate right/bottom
32394             if(x + w > vw+s.left){
32395                 x = vw - w;
32396                 moved = true;
32397             }
32398             if(y + h > vh+s.top){
32399                 y = vh - h;
32400                 moved = true;
32401             }
32402             // then make sure top/left isn't negative
32403             if(x < s.left){
32404                 x = s.left;
32405                 moved = true;
32406             }
32407             if(y < s.top){
32408                 y = s.top;
32409                 moved = true;
32410             }
32411             if(moved){
32412                 // cache xy
32413                 this.xy = [x, y];
32414                 if(this.isVisible()){
32415                     this.el.setLocation(x, y);
32416                     this.adjustAssets();
32417                 }
32418             }
32419         }
32420     },
32421
32422     // private
32423     onDrag : function(){
32424         if(!this.proxyDrag){
32425             this.xy = this.el.getXY();
32426             this.adjustAssets();
32427         }
32428     },
32429
32430     // private
32431     adjustAssets : function(doShow){
32432         var x = this.xy[0], y = this.xy[1];
32433         var w = this.size.width, h = this.size.height;
32434         if(doShow === true){
32435             if(this.shadow){
32436                 this.shadow.show(this.el);
32437             }
32438             if(this.shim){
32439                 this.shim.show();
32440             }
32441         }
32442         if(this.shadow && this.shadow.isVisible()){
32443             this.shadow.show(this.el);
32444         }
32445         if(this.shim && this.shim.isVisible()){
32446             this.shim.setBounds(x, y, w, h);
32447         }
32448     },
32449
32450     // private
32451     adjustViewport : function(w, h){
32452         if(!w || !h){
32453             w = Roo.lib.Dom.getViewWidth();
32454             h = Roo.lib.Dom.getViewHeight();
32455         }
32456         // cache the size
32457         this.viewSize = [w, h];
32458         if(this.modal && this.mask.isVisible()){
32459             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32460             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32461         }
32462         if(this.isVisible()){
32463             this.constrainXY();
32464         }
32465     },
32466
32467     /**
32468      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32469      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32470      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32471      */
32472     destroy : function(removeEl){
32473         if(this.isVisible()){
32474             this.animateTarget = null;
32475             this.hide();
32476         }
32477         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32478         if(this.tabs){
32479             this.tabs.destroy(removeEl);
32480         }
32481         Roo.destroy(
32482              this.shim,
32483              this.proxy,
32484              this.resizer,
32485              this.close,
32486              this.mask
32487         );
32488         if(this.dd){
32489             this.dd.unreg();
32490         }
32491         if(this.buttons){
32492            for(var i = 0, len = this.buttons.length; i < len; i++){
32493                this.buttons[i].destroy();
32494            }
32495         }
32496         this.el.removeAllListeners();
32497         if(removeEl === true){
32498             this.el.update("");
32499             this.el.remove();
32500         }
32501         Roo.DialogManager.unregister(this);
32502     },
32503
32504     // private
32505     startMove : function(){
32506         if(this.proxyDrag){
32507             this.proxy.show();
32508         }
32509         if(this.constraintoviewport !== false){
32510             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32511         }
32512     },
32513
32514     // private
32515     endMove : function(){
32516         if(!this.proxyDrag){
32517             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32518         }else{
32519             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32520             this.proxy.hide();
32521         }
32522         this.refreshSize();
32523         this.adjustAssets();
32524         this.focus();
32525         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32526     },
32527
32528     /**
32529      * Brings this dialog to the front of any other visible dialogs
32530      * @return {Roo.BasicDialog} this
32531      */
32532     toFront : function(){
32533         Roo.DialogManager.bringToFront(this);
32534         return this;
32535     },
32536
32537     /**
32538      * Sends this dialog to the back (under) of any other visible dialogs
32539      * @return {Roo.BasicDialog} this
32540      */
32541     toBack : function(){
32542         Roo.DialogManager.sendToBack(this);
32543         return this;
32544     },
32545
32546     /**
32547      * Centers this dialog in the viewport
32548      * @return {Roo.BasicDialog} this
32549      */
32550     center : function(){
32551         var xy = this.el.getCenterXY(true);
32552         this.moveTo(xy[0], xy[1]);
32553         return this;
32554     },
32555
32556     /**
32557      * Moves the dialog's top-left corner to the specified point
32558      * @param {Number} x
32559      * @param {Number} y
32560      * @return {Roo.BasicDialog} this
32561      */
32562     moveTo : function(x, y){
32563         this.xy = [x,y];
32564         if(this.isVisible()){
32565             this.el.setXY(this.xy);
32566             this.adjustAssets();
32567         }
32568         return this;
32569     },
32570
32571     /**
32572      * Aligns the dialog to the specified element
32573      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32574      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32575      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32576      * @return {Roo.BasicDialog} this
32577      */
32578     alignTo : function(element, position, offsets){
32579         this.xy = this.el.getAlignToXY(element, position, offsets);
32580         if(this.isVisible()){
32581             this.el.setXY(this.xy);
32582             this.adjustAssets();
32583         }
32584         return this;
32585     },
32586
32587     /**
32588      * Anchors an element to another element and realigns it when the window is resized.
32589      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32590      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32591      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32592      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32593      * is a number, it is used as the buffer delay (defaults to 50ms).
32594      * @return {Roo.BasicDialog} this
32595      */
32596     anchorTo : function(el, alignment, offsets, monitorScroll){
32597         var action = function(){
32598             this.alignTo(el, alignment, offsets);
32599         };
32600         Roo.EventManager.onWindowResize(action, this);
32601         var tm = typeof monitorScroll;
32602         if(tm != 'undefined'){
32603             Roo.EventManager.on(window, 'scroll', action, this,
32604                 {buffer: tm == 'number' ? monitorScroll : 50});
32605         }
32606         action.call(this);
32607         return this;
32608     },
32609
32610     /**
32611      * Returns true if the dialog is visible
32612      * @return {Boolean}
32613      */
32614     isVisible : function(){
32615         return this.el.isVisible();
32616     },
32617
32618     // private
32619     animHide : function(callback){
32620         var b = Roo.get(this.animateTarget).getBox();
32621         this.proxy.show();
32622         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32623         this.el.hide();
32624         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32625                     this.hideEl.createDelegate(this, [callback]));
32626     },
32627
32628     /**
32629      * Hides the dialog.
32630      * @param {Function} callback (optional) Function to call when the dialog is hidden
32631      * @return {Roo.BasicDialog} this
32632      */
32633     hide : function(callback){
32634         if (this.fireEvent("beforehide", this) === false){
32635             return;
32636         }
32637         if(this.shadow){
32638             this.shadow.hide();
32639         }
32640         if(this.shim) {
32641           this.shim.hide();
32642         }
32643         // sometimes animateTarget seems to get set.. causing problems...
32644         // this just double checks..
32645         if(this.animateTarget && Roo.get(this.animateTarget)) {
32646            this.animHide(callback);
32647         }else{
32648             this.el.hide();
32649             this.hideEl(callback);
32650         }
32651         return this;
32652     },
32653
32654     // private
32655     hideEl : function(callback){
32656         this.proxy.hide();
32657         if(this.modal){
32658             this.mask.hide();
32659             Roo.get(document.body).removeClass("x-body-masked");
32660         }
32661         this.fireEvent("hide", this);
32662         if(typeof callback == "function"){
32663             callback();
32664         }
32665     },
32666
32667     // private
32668     hideAction : function(){
32669         this.setLeft("-10000px");
32670         this.setTop("-10000px");
32671         this.setStyle("visibility", "hidden");
32672     },
32673
32674     // private
32675     refreshSize : function(){
32676         this.size = this.el.getSize();
32677         this.xy = this.el.getXY();
32678         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32679     },
32680
32681     // private
32682     // z-index is managed by the DialogManager and may be overwritten at any time
32683     setZIndex : function(index){
32684         if(this.modal){
32685             this.mask.setStyle("z-index", index);
32686         }
32687         if(this.shim){
32688             this.shim.setStyle("z-index", ++index);
32689         }
32690         if(this.shadow){
32691             this.shadow.setZIndex(++index);
32692         }
32693         this.el.setStyle("z-index", ++index);
32694         if(this.proxy){
32695             this.proxy.setStyle("z-index", ++index);
32696         }
32697         if(this.resizer){
32698             this.resizer.proxy.setStyle("z-index", ++index);
32699         }
32700
32701         this.lastZIndex = index;
32702     },
32703
32704     /**
32705      * Returns the element for this dialog
32706      * @return {Roo.Element} The underlying dialog Element
32707      */
32708     getEl : function(){
32709         return this.el;
32710     }
32711 });
32712
32713 /**
32714  * @class Roo.DialogManager
32715  * Provides global access to BasicDialogs that have been created and
32716  * support for z-indexing (layering) multiple open dialogs.
32717  */
32718 Roo.DialogManager = function(){
32719     var list = {};
32720     var accessList = [];
32721     var front = null;
32722
32723     // private
32724     var sortDialogs = function(d1, d2){
32725         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32726     };
32727
32728     // private
32729     var orderDialogs = function(){
32730         accessList.sort(sortDialogs);
32731         var seed = Roo.DialogManager.zseed;
32732         for(var i = 0, len = accessList.length; i < len; i++){
32733             var dlg = accessList[i];
32734             if(dlg){
32735                 dlg.setZIndex(seed + (i*10));
32736             }
32737         }
32738     };
32739
32740     return {
32741         /**
32742          * The starting z-index for BasicDialogs (defaults to 9000)
32743          * @type Number The z-index value
32744          */
32745         zseed : 9000,
32746
32747         // private
32748         register : function(dlg){
32749             list[dlg.id] = dlg;
32750             accessList.push(dlg);
32751         },
32752
32753         // private
32754         unregister : function(dlg){
32755             delete list[dlg.id];
32756             var i=0;
32757             var len=0;
32758             if(!accessList.indexOf){
32759                 for(  i = 0, len = accessList.length; i < len; i++){
32760                     if(accessList[i] == dlg){
32761                         accessList.splice(i, 1);
32762                         return;
32763                     }
32764                 }
32765             }else{
32766                  i = accessList.indexOf(dlg);
32767                 if(i != -1){
32768                     accessList.splice(i, 1);
32769                 }
32770             }
32771         },
32772
32773         /**
32774          * Gets a registered dialog by id
32775          * @param {String/Object} id The id of the dialog or a dialog
32776          * @return {Roo.BasicDialog} this
32777          */
32778         get : function(id){
32779             return typeof id == "object" ? id : list[id];
32780         },
32781
32782         /**
32783          * Brings the specified dialog to the front
32784          * @param {String/Object} dlg The id of the dialog or a dialog
32785          * @return {Roo.BasicDialog} this
32786          */
32787         bringToFront : function(dlg){
32788             dlg = this.get(dlg);
32789             if(dlg != front){
32790                 front = dlg;
32791                 dlg._lastAccess = new Date().getTime();
32792                 orderDialogs();
32793             }
32794             return dlg;
32795         },
32796
32797         /**
32798          * Sends the specified dialog to the back
32799          * @param {String/Object} dlg The id of the dialog or a dialog
32800          * @return {Roo.BasicDialog} this
32801          */
32802         sendToBack : function(dlg){
32803             dlg = this.get(dlg);
32804             dlg._lastAccess = -(new Date().getTime());
32805             orderDialogs();
32806             return dlg;
32807         },
32808
32809         /**
32810          * Hides all dialogs
32811          */
32812         hideAll : function(){
32813             for(var id in list){
32814                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32815                     list[id].hide();
32816                 }
32817             }
32818         }
32819     };
32820 }();
32821
32822 /**
32823  * @class Roo.LayoutDialog
32824  * @extends Roo.BasicDialog
32825  * Dialog which provides adjustments for working with a layout in a Dialog.
32826  * Add your necessary layout config options to the dialog's config.<br>
32827  * Example usage (including a nested layout):
32828  * <pre><code>
32829 if(!dialog){
32830     dialog = new Roo.LayoutDialog("download-dlg", {
32831         modal: true,
32832         width:600,
32833         height:450,
32834         shadow:true,
32835         minWidth:500,
32836         minHeight:350,
32837         autoTabs:true,
32838         proxyDrag:true,
32839         // layout config merges with the dialog config
32840         center:{
32841             tabPosition: "top",
32842             alwaysShowTabs: true
32843         }
32844     });
32845     dialog.addKeyListener(27, dialog.hide, dialog);
32846     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32847     dialog.addButton("Build It!", this.getDownload, this);
32848
32849     // we can even add nested layouts
32850     var innerLayout = new Roo.BorderLayout("dl-inner", {
32851         east: {
32852             initialSize: 200,
32853             autoScroll:true,
32854             split:true
32855         },
32856         center: {
32857             autoScroll:true
32858         }
32859     });
32860     innerLayout.beginUpdate();
32861     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32862     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32863     innerLayout.endUpdate(true);
32864
32865     var layout = dialog.getLayout();
32866     layout.beginUpdate();
32867     layout.add("center", new Roo.ContentPanel("standard-panel",
32868                         {title: "Download the Source", fitToFrame:true}));
32869     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32870                {title: "Build your own roo.js"}));
32871     layout.getRegion("center").showPanel(sp);
32872     layout.endUpdate();
32873 }
32874 </code></pre>
32875     * @constructor
32876     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32877     * @param {Object} config configuration options
32878   */
32879 Roo.LayoutDialog = function(el, cfg){
32880     
32881     var config=  cfg;
32882     if (typeof(cfg) == 'undefined') {
32883         config = Roo.apply({}, el);
32884         // not sure why we use documentElement here.. - it should always be body.
32885         // IE7 borks horribly if we use documentElement.
32886         // webkit also does not like documentElement - it creates a body element...
32887         el = Roo.get( document.body || document.documentElement ).createChild();
32888         //config.autoCreate = true;
32889     }
32890     
32891     
32892     config.autoTabs = false;
32893     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32894     this.body.setStyle({overflow:"hidden", position:"relative"});
32895     this.layout = new Roo.BorderLayout(this.body.dom, config);
32896     this.layout.monitorWindowResize = false;
32897     this.el.addClass("x-dlg-auto-layout");
32898     // fix case when center region overwrites center function
32899     this.center = Roo.BasicDialog.prototype.center;
32900     this.on("show", this.layout.layout, this.layout, true);
32901     if (config.items) {
32902         var xitems = config.items;
32903         delete config.items;
32904         Roo.each(xitems, this.addxtype, this);
32905     }
32906     
32907     
32908 };
32909 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32910     /**
32911      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32912      * @deprecated
32913      */
32914     endUpdate : function(){
32915         this.layout.endUpdate();
32916     },
32917
32918     /**
32919      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32920      *  @deprecated
32921      */
32922     beginUpdate : function(){
32923         this.layout.beginUpdate();
32924     },
32925
32926     /**
32927      * Get the BorderLayout for this dialog
32928      * @return {Roo.BorderLayout}
32929      */
32930     getLayout : function(){
32931         return this.layout;
32932     },
32933
32934     showEl : function(){
32935         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32936         if(Roo.isIE7){
32937             this.layout.layout();
32938         }
32939     },
32940
32941     // private
32942     // Use the syncHeightBeforeShow config option to control this automatically
32943     syncBodyHeight : function(){
32944         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32945         if(this.layout){this.layout.layout();}
32946     },
32947     
32948       /**
32949      * Add an xtype element (actually adds to the layout.)
32950      * @return {Object} xdata xtype object data.
32951      */
32952     
32953     addxtype : function(c) {
32954         return this.layout.addxtype(c);
32955     }
32956 });/*
32957  * Based on:
32958  * Ext JS Library 1.1.1
32959  * Copyright(c) 2006-2007, Ext JS, LLC.
32960  *
32961  * Originally Released Under LGPL - original licence link has changed is not relivant.
32962  *
32963  * Fork - LGPL
32964  * <script type="text/javascript">
32965  */
32966  
32967 /**
32968  * @class Roo.MessageBox
32969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32970  * Example usage:
32971  *<pre><code>
32972 // Basic alert:
32973 Roo.Msg.alert('Status', 'Changes saved successfully.');
32974
32975 // Prompt for user data:
32976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32977     if (btn == 'ok'){
32978         // process text value...
32979     }
32980 });
32981
32982 // Show a dialog using config options:
32983 Roo.Msg.show({
32984    title:'Save Changes?',
32985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32986    buttons: Roo.Msg.YESNOCANCEL,
32987    fn: processResult,
32988    animEl: 'elId'
32989 });
32990 </code></pre>
32991  * @singleton
32992  */
32993 Roo.MessageBox = function(){
32994     var dlg, opt, mask, waitTimer;
32995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32996     var buttons, activeTextEl, bwidth;
32997
32998     // private
32999     var handleButton = function(button){
33000         dlg.hide();
33001         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33002     };
33003
33004     // private
33005     var handleHide = function(){
33006         if(opt && opt.cls){
33007             dlg.el.removeClass(opt.cls);
33008         }
33009         if(waitTimer){
33010             Roo.TaskMgr.stop(waitTimer);
33011             waitTimer = null;
33012         }
33013     };
33014
33015     // private
33016     var updateButtons = function(b){
33017         var width = 0;
33018         if(!b){
33019             buttons["ok"].hide();
33020             buttons["cancel"].hide();
33021             buttons["yes"].hide();
33022             buttons["no"].hide();
33023             dlg.footer.dom.style.display = 'none';
33024             return width;
33025         }
33026         dlg.footer.dom.style.display = '';
33027         for(var k in buttons){
33028             if(typeof buttons[k] != "function"){
33029                 if(b[k]){
33030                     buttons[k].show();
33031                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33032                     width += buttons[k].el.getWidth()+15;
33033                 }else{
33034                     buttons[k].hide();
33035                 }
33036             }
33037         }
33038         return width;
33039     };
33040
33041     // private
33042     var handleEsc = function(d, k, e){
33043         if(opt && opt.closable !== false){
33044             dlg.hide();
33045         }
33046         if(e){
33047             e.stopEvent();
33048         }
33049     };
33050
33051     return {
33052         /**
33053          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33054          * @return {Roo.BasicDialog} The BasicDialog element
33055          */
33056         getDialog : function(){
33057            if(!dlg){
33058                 dlg = new Roo.BasicDialog("x-msg-box", {
33059                     autoCreate : true,
33060                     shadow: true,
33061                     draggable: true,
33062                     resizable:false,
33063                     constraintoviewport:false,
33064                     fixedcenter:true,
33065                     collapsible : false,
33066                     shim:true,
33067                     modal: true,
33068                     width:400, height:100,
33069                     buttonAlign:"center",
33070                     closeClick : function(){
33071                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33072                             handleButton("no");
33073                         }else{
33074                             handleButton("cancel");
33075                         }
33076                     }
33077                 });
33078                 dlg.on("hide", handleHide);
33079                 mask = dlg.mask;
33080                 dlg.addKeyListener(27, handleEsc);
33081                 buttons = {};
33082                 var bt = this.buttonText;
33083                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33084                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33085                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33086                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33087                 bodyEl = dlg.body.createChild({
33088
33089                     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>'
33090                 });
33091                 msgEl = bodyEl.dom.firstChild;
33092                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33093                 textboxEl.enableDisplayMode();
33094                 textboxEl.addKeyListener([10,13], function(){
33095                     if(dlg.isVisible() && opt && opt.buttons){
33096                         if(opt.buttons.ok){
33097                             handleButton("ok");
33098                         }else if(opt.buttons.yes){
33099                             handleButton("yes");
33100                         }
33101                     }
33102                 });
33103                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33104                 textareaEl.enableDisplayMode();
33105                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33106                 progressEl.enableDisplayMode();
33107                 var pf = progressEl.dom.firstChild;
33108                 if (pf) {
33109                     pp = Roo.get(pf.firstChild);
33110                     pp.setHeight(pf.offsetHeight);
33111                 }
33112                 
33113             }
33114             return dlg;
33115         },
33116
33117         /**
33118          * Updates the message box body text
33119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33120          * the XHTML-compliant non-breaking space character '&amp;#160;')
33121          * @return {Roo.MessageBox} This message box
33122          */
33123         updateText : function(text){
33124             if(!dlg.isVisible() && !opt.width){
33125                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33126             }
33127             msgEl.innerHTML = text || '&#160;';
33128       
33129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33131             var w = Math.max(
33132                     Math.min(opt.width || cw , this.maxWidth), 
33133                     Math.max(opt.minWidth || this.minWidth, bwidth)
33134             );
33135             if(opt.prompt){
33136                 activeTextEl.setWidth(w);
33137             }
33138             if(dlg.isVisible()){
33139                 dlg.fixedcenter = false;
33140             }
33141             // to big, make it scroll. = But as usual stupid IE does not support
33142             // !important..
33143             
33144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33147             } else {
33148                 bodyEl.dom.style.height = '';
33149                 bodyEl.dom.style.overflowY = '';
33150             }
33151             if (cw > w) {
33152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33153             } else {
33154                 bodyEl.dom.style.overflowX = '';
33155             }
33156             
33157             dlg.setContentSize(w, bodyEl.getHeight());
33158             if(dlg.isVisible()){
33159                 dlg.fixedcenter = true;
33160             }
33161             return this;
33162         },
33163
33164         /**
33165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33169          * @return {Roo.MessageBox} This message box
33170          */
33171         updateProgress : function(value, text){
33172             if(text){
33173                 this.updateText(text);
33174             }
33175             if (pp) { // weird bug on my firefox - for some reason this is not defined
33176                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33177             }
33178             return this;
33179         },        
33180
33181         /**
33182          * Returns true if the message box is currently displayed
33183          * @return {Boolean} True if the message box is visible, else false
33184          */
33185         isVisible : function(){
33186             return dlg && dlg.isVisible();  
33187         },
33188
33189         /**
33190          * Hides the message box if it is displayed
33191          */
33192         hide : function(){
33193             if(this.isVisible()){
33194                 dlg.hide();
33195             }  
33196         },
33197
33198         /**
33199          * Displays a new message box, or reinitializes an existing message box, based on the config options
33200          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33201          * The following config object properties are supported:
33202          * <pre>
33203 Property    Type             Description
33204 ----------  ---------------  ------------------------------------------------------------------------------------
33205 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33206                                    closes (defaults to undefined)
33207 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33208                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33209 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33210                                    progress and wait dialogs will ignore this property and always hide the
33211                                    close button as they can only be closed programmatically.
33212 cls               String           A custom CSS class to apply to the message box element
33213 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33214                                    displayed (defaults to 75)
33215 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33216                                    function will be btn (the name of the button that was clicked, if applicable,
33217                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33218                                    Progress and wait dialogs will ignore this option since they do not respond to
33219                                    user actions and can only be closed programmatically, so any required function
33220                                    should be called by the same code after it closes the dialog.
33221 icon              String           A CSS class that provides a background image to be used as an icon for
33222                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33223 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33224 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33225 modal             Boolean          False to allow user interaction with the page while the message box is
33226                                    displayed (defaults to true)
33227 msg               String           A string that will replace the existing message box body text (defaults
33228                                    to the XHTML-compliant non-breaking space character '&#160;')
33229 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33230 progress          Boolean          True to display a progress bar (defaults to false)
33231 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33232 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33233 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33234 title             String           The title text
33235 value             String           The string value to set into the active textbox element if displayed
33236 wait              Boolean          True to display a progress bar (defaults to false)
33237 width             Number           The width of the dialog in pixels
33238 </pre>
33239          *
33240          * Example usage:
33241          * <pre><code>
33242 Roo.Msg.show({
33243    title: 'Address',
33244    msg: 'Please enter your address:',
33245    width: 300,
33246    buttons: Roo.MessageBox.OKCANCEL,
33247    multiline: true,
33248    fn: saveAddress,
33249    animEl: 'addAddressBtn'
33250 });
33251 </code></pre>
33252          * @param {Object} config Configuration options
33253          * @return {Roo.MessageBox} This message box
33254          */
33255         show : function(options)
33256         {
33257             
33258             // this causes nightmares if you show one dialog after another
33259             // especially on callbacks..
33260              
33261             if(this.isVisible()){
33262                 
33263                 this.hide();
33264                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33265                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33266                 Roo.log("New Dialog Message:" +  options.msg )
33267                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33268                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33269                 
33270             }
33271             var d = this.getDialog();
33272             opt = options;
33273             d.setTitle(opt.title || "&#160;");
33274             d.close.setDisplayed(opt.closable !== false);
33275             activeTextEl = textboxEl;
33276             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33277             if(opt.prompt){
33278                 if(opt.multiline){
33279                     textboxEl.hide();
33280                     textareaEl.show();
33281                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33282                         opt.multiline : this.defaultTextHeight);
33283                     activeTextEl = textareaEl;
33284                 }else{
33285                     textboxEl.show();
33286                     textareaEl.hide();
33287                 }
33288             }else{
33289                 textboxEl.hide();
33290                 textareaEl.hide();
33291             }
33292             progressEl.setDisplayed(opt.progress === true);
33293             this.updateProgress(0);
33294             activeTextEl.dom.value = opt.value || "";
33295             if(opt.prompt){
33296                 dlg.setDefaultButton(activeTextEl);
33297             }else{
33298                 var bs = opt.buttons;
33299                 var db = null;
33300                 if(bs && bs.ok){
33301                     db = buttons["ok"];
33302                 }else if(bs && bs.yes){
33303                     db = buttons["yes"];
33304                 }
33305                 dlg.setDefaultButton(db);
33306             }
33307             bwidth = updateButtons(opt.buttons);
33308             this.updateText(opt.msg);
33309             if(opt.cls){
33310                 d.el.addClass(opt.cls);
33311             }
33312             d.proxyDrag = opt.proxyDrag === true;
33313             d.modal = opt.modal !== false;
33314             d.mask = opt.modal !== false ? mask : false;
33315             if(!d.isVisible()){
33316                 // force it to the end of the z-index stack so it gets a cursor in FF
33317                 document.body.appendChild(dlg.el.dom);
33318                 d.animateTarget = null;
33319                 d.show(options.animEl);
33320             }
33321             return this;
33322         },
33323
33324         /**
33325          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33326          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33327          * and closing the message box when the process is complete.
33328          * @param {String} title The title bar text
33329          * @param {String} msg The message box body text
33330          * @return {Roo.MessageBox} This message box
33331          */
33332         progress : function(title, msg){
33333             this.show({
33334                 title : title,
33335                 msg : msg,
33336                 buttons: false,
33337                 progress:true,
33338                 closable:false,
33339                 minWidth: this.minProgressWidth,
33340                 modal : true
33341             });
33342             return this;
33343         },
33344
33345         /**
33346          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33347          * If a callback function is passed it will be called after the user clicks the button, and the
33348          * id of the button that was clicked will be passed as the only parameter to the callback
33349          * (could also be the top-right close button).
33350          * @param {String} title The title bar text
33351          * @param {String} msg The message box body text
33352          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33353          * @param {Object} scope (optional) The scope of the callback function
33354          * @return {Roo.MessageBox} This message box
33355          */
33356         alert : function(title, msg, fn, scope){
33357             this.show({
33358                 title : title,
33359                 msg : msg,
33360                 buttons: this.OK,
33361                 fn: fn,
33362                 scope : scope,
33363                 modal : true
33364             });
33365             return this;
33366         },
33367
33368         /**
33369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33371          * You are responsible for closing the message box when the process is complete.
33372          * @param {String} msg The message box body text
33373          * @param {String} title (optional) The title bar text
33374          * @return {Roo.MessageBox} This message box
33375          */
33376         wait : function(msg, title){
33377             this.show({
33378                 title : title,
33379                 msg : msg,
33380                 buttons: false,
33381                 closable:false,
33382                 progress:true,
33383                 modal:true,
33384                 width:300,
33385                 wait:true
33386             });
33387             waitTimer = Roo.TaskMgr.start({
33388                 run: function(i){
33389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33390                 },
33391                 interval: 1000
33392             });
33393             return this;
33394         },
33395
33396         /**
33397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33400          * @param {String} title The title bar text
33401          * @param {String} msg The message box body text
33402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33403          * @param {Object} scope (optional) The scope of the callback function
33404          * @return {Roo.MessageBox} This message box
33405          */
33406         confirm : function(title, msg, fn, scope){
33407             this.show({
33408                 title : title,
33409                 msg : msg,
33410                 buttons: this.YESNO,
33411                 fn: fn,
33412                 scope : scope,
33413                 modal : true
33414             });
33415             return this;
33416         },
33417
33418         /**
33419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33422          * (could also be the top-right close button) and the text that was entered will be passed as the two
33423          * parameters to the callback.
33424          * @param {String} title The title bar text
33425          * @param {String} msg The message box body text
33426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33427          * @param {Object} scope (optional) The scope of the callback function
33428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33430          * @return {Roo.MessageBox} This message box
33431          */
33432         prompt : function(title, msg, fn, scope, multiline){
33433             this.show({
33434                 title : title,
33435                 msg : msg,
33436                 buttons: this.OKCANCEL,
33437                 fn: fn,
33438                 minWidth:250,
33439                 scope : scope,
33440                 prompt:true,
33441                 multiline: multiline,
33442                 modal : true
33443             });
33444             return this;
33445         },
33446
33447         /**
33448          * Button config that displays a single OK button
33449          * @type Object
33450          */
33451         OK : {ok:true},
33452         /**
33453          * Button config that displays Yes and No buttons
33454          * @type Object
33455          */
33456         YESNO : {yes:true, no:true},
33457         /**
33458          * Button config that displays OK and Cancel buttons
33459          * @type Object
33460          */
33461         OKCANCEL : {ok:true, cancel:true},
33462         /**
33463          * Button config that displays Yes, No and Cancel buttons
33464          * @type Object
33465          */
33466         YESNOCANCEL : {yes:true, no:true, cancel:true},
33467
33468         /**
33469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33470          * @type Number
33471          */
33472         defaultTextHeight : 75,
33473         /**
33474          * The maximum width in pixels of the message box (defaults to 600)
33475          * @type Number
33476          */
33477         maxWidth : 600,
33478         /**
33479          * The minimum width in pixels of the message box (defaults to 100)
33480          * @type Number
33481          */
33482         minWidth : 100,
33483         /**
33484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33486          * @type Number
33487          */
33488         minProgressWidth : 250,
33489         /**
33490          * An object containing the default button text strings that can be overriden for localized language support.
33491          * Supported properties are: ok, cancel, yes and no.
33492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33493          * @type Object
33494          */
33495         buttonText : {
33496             ok : "OK",
33497             cancel : "Cancel",
33498             yes : "Yes",
33499             no : "No"
33500         }
33501     };
33502 }();
33503
33504 /**
33505  * Shorthand for {@link Roo.MessageBox}
33506  */
33507 Roo.Msg = Roo.MessageBox;/*
33508  * Based on:
33509  * Ext JS Library 1.1.1
33510  * Copyright(c) 2006-2007, Ext JS, LLC.
33511  *
33512  * Originally Released Under LGPL - original licence link has changed is not relivant.
33513  *
33514  * Fork - LGPL
33515  * <script type="text/javascript">
33516  */
33517 /**
33518  * @class Roo.QuickTips
33519  * Provides attractive and customizable tooltips for any element.
33520  * @singleton
33521  */
33522 Roo.QuickTips = function(){
33523     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33524     var ce, bd, xy, dd;
33525     var visible = false, disabled = true, inited = false;
33526     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33527     
33528     var onOver = function(e){
33529         if(disabled){
33530             return;
33531         }
33532         var t = e.getTarget();
33533         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33534             return;
33535         }
33536         if(ce && t == ce.el){
33537             clearTimeout(hideProc);
33538             return;
33539         }
33540         if(t && tagEls[t.id]){
33541             tagEls[t.id].el = t;
33542             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33543             return;
33544         }
33545         var ttp, et = Roo.fly(t);
33546         var ns = cfg.namespace;
33547         if(tm.interceptTitles && t.title){
33548             ttp = t.title;
33549             t.qtip = ttp;
33550             t.removeAttribute("title");
33551             e.preventDefault();
33552         }else{
33553             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33554         }
33555         if(ttp){
33556             showProc = show.defer(tm.showDelay, tm, [{
33557                 el: t, 
33558                 text: ttp, 
33559                 width: et.getAttributeNS(ns, cfg.width),
33560                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33561                 title: et.getAttributeNS(ns, cfg.title),
33562                     cls: et.getAttributeNS(ns, cfg.cls)
33563             }]);
33564         }
33565     };
33566     
33567     var onOut = function(e){
33568         clearTimeout(showProc);
33569         var t = e.getTarget();
33570         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33571             hideProc = setTimeout(hide, tm.hideDelay);
33572         }
33573     };
33574     
33575     var onMove = function(e){
33576         if(disabled){
33577             return;
33578         }
33579         xy = e.getXY();
33580         xy[1] += 18;
33581         if(tm.trackMouse && ce){
33582             el.setXY(xy);
33583         }
33584     };
33585     
33586     var onDown = function(e){
33587         clearTimeout(showProc);
33588         clearTimeout(hideProc);
33589         if(!e.within(el)){
33590             if(tm.hideOnClick){
33591                 hide();
33592                 tm.disable();
33593                 tm.enable.defer(100, tm);
33594             }
33595         }
33596     };
33597     
33598     var getPad = function(){
33599         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33600     };
33601
33602     var show = function(o){
33603         if(disabled){
33604             return;
33605         }
33606         clearTimeout(dismissProc);
33607         ce = o;
33608         if(removeCls){ // in case manually hidden
33609             el.removeClass(removeCls);
33610             removeCls = null;
33611         }
33612         if(ce.cls){
33613             el.addClass(ce.cls);
33614             removeCls = ce.cls;
33615         }
33616         if(ce.title){
33617             tipTitle.update(ce.title);
33618             tipTitle.show();
33619         }else{
33620             tipTitle.update('');
33621             tipTitle.hide();
33622         }
33623         el.dom.style.width  = tm.maxWidth+'px';
33624         //tipBody.dom.style.width = '';
33625         tipBodyText.update(o.text);
33626         var p = getPad(), w = ce.width;
33627         if(!w){
33628             var td = tipBodyText.dom;
33629             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33630             if(aw > tm.maxWidth){
33631                 w = tm.maxWidth;
33632             }else if(aw < tm.minWidth){
33633                 w = tm.minWidth;
33634             }else{
33635                 w = aw;
33636             }
33637         }
33638         //tipBody.setWidth(w);
33639         el.setWidth(parseInt(w, 10) + p);
33640         if(ce.autoHide === false){
33641             close.setDisplayed(true);
33642             if(dd){
33643                 dd.unlock();
33644             }
33645         }else{
33646             close.setDisplayed(false);
33647             if(dd){
33648                 dd.lock();
33649             }
33650         }
33651         if(xy){
33652             el.avoidY = xy[1]-18;
33653             el.setXY(xy);
33654         }
33655         if(tm.animate){
33656             el.setOpacity(.1);
33657             el.setStyle("visibility", "visible");
33658             el.fadeIn({callback: afterShow});
33659         }else{
33660             afterShow();
33661         }
33662     };
33663     
33664     var afterShow = function(){
33665         if(ce){
33666             el.show();
33667             esc.enable();
33668             if(tm.autoDismiss && ce.autoHide !== false){
33669                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33670             }
33671         }
33672     };
33673     
33674     var hide = function(noanim){
33675         clearTimeout(dismissProc);
33676         clearTimeout(hideProc);
33677         ce = null;
33678         if(el.isVisible()){
33679             esc.disable();
33680             if(noanim !== true && tm.animate){
33681                 el.fadeOut({callback: afterHide});
33682             }else{
33683                 afterHide();
33684             } 
33685         }
33686     };
33687     
33688     var afterHide = function(){
33689         el.hide();
33690         if(removeCls){
33691             el.removeClass(removeCls);
33692             removeCls = null;
33693         }
33694     };
33695     
33696     return {
33697         /**
33698         * @cfg {Number} minWidth
33699         * The minimum width of the quick tip (defaults to 40)
33700         */
33701        minWidth : 40,
33702         /**
33703         * @cfg {Number} maxWidth
33704         * The maximum width of the quick tip (defaults to 300)
33705         */
33706        maxWidth : 300,
33707         /**
33708         * @cfg {Boolean} interceptTitles
33709         * True to automatically use the element's DOM title value if available (defaults to false)
33710         */
33711        interceptTitles : false,
33712         /**
33713         * @cfg {Boolean} trackMouse
33714         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33715         */
33716        trackMouse : false,
33717         /**
33718         * @cfg {Boolean} hideOnClick
33719         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33720         */
33721        hideOnClick : true,
33722         /**
33723         * @cfg {Number} showDelay
33724         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33725         */
33726        showDelay : 500,
33727         /**
33728         * @cfg {Number} hideDelay
33729         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33730         */
33731        hideDelay : 200,
33732         /**
33733         * @cfg {Boolean} autoHide
33734         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33735         * Used in conjunction with hideDelay.
33736         */
33737        autoHide : true,
33738         /**
33739         * @cfg {Boolean}
33740         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33741         * (defaults to true).  Used in conjunction with autoDismissDelay.
33742         */
33743        autoDismiss : true,
33744         /**
33745         * @cfg {Number}
33746         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33747         */
33748        autoDismissDelay : 5000,
33749        /**
33750         * @cfg {Boolean} animate
33751         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33752         */
33753        animate : false,
33754
33755        /**
33756         * @cfg {String} title
33757         * Title text to display (defaults to '').  This can be any valid HTML markup.
33758         */
33759         title: '',
33760        /**
33761         * @cfg {String} text
33762         * Body text to display (defaults to '').  This can be any valid HTML markup.
33763         */
33764         text : '',
33765        /**
33766         * @cfg {String} cls
33767         * A CSS class to apply to the base quick tip element (defaults to '').
33768         */
33769         cls : '',
33770        /**
33771         * @cfg {Number} width
33772         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33773         * minWidth or maxWidth.
33774         */
33775         width : null,
33776
33777     /**
33778      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33779      * or display QuickTips in a page.
33780      */
33781        init : function(){
33782           tm = Roo.QuickTips;
33783           cfg = tm.tagConfig;
33784           if(!inited){
33785               if(!Roo.isReady){ // allow calling of init() before onReady
33786                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33787                   return;
33788               }
33789               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33790               el.fxDefaults = {stopFx: true};
33791               // maximum custom styling
33792               //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>');
33793               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>');              
33794               tipTitle = el.child('h3');
33795               tipTitle.enableDisplayMode("block");
33796               tipBody = el.child('div.x-tip-bd');
33797               tipBodyText = el.child('div.x-tip-bd-inner');
33798               //bdLeft = el.child('div.x-tip-bd-left');
33799               //bdRight = el.child('div.x-tip-bd-right');
33800               close = el.child('div.x-tip-close');
33801               close.enableDisplayMode("block");
33802               close.on("click", hide);
33803               var d = Roo.get(document);
33804               d.on("mousedown", onDown);
33805               d.on("mouseover", onOver);
33806               d.on("mouseout", onOut);
33807               d.on("mousemove", onMove);
33808               esc = d.addKeyListener(27, hide);
33809               esc.disable();
33810               if(Roo.dd.DD){
33811                   dd = el.initDD("default", null, {
33812                       onDrag : function(){
33813                           el.sync();  
33814                       }
33815                   });
33816                   dd.setHandleElId(tipTitle.id);
33817                   dd.lock();
33818               }
33819               inited = true;
33820           }
33821           this.enable(); 
33822        },
33823
33824     /**
33825      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33826      * are supported:
33827      * <pre>
33828 Property    Type                   Description
33829 ----------  ---------------------  ------------------------------------------------------------------------
33830 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33831      * </ul>
33832      * @param {Object} config The config object
33833      */
33834        register : function(config){
33835            var cs = config instanceof Array ? config : arguments;
33836            for(var i = 0, len = cs.length; i < len; i++) {
33837                var c = cs[i];
33838                var target = c.target;
33839                if(target){
33840                    if(target instanceof Array){
33841                        for(var j = 0, jlen = target.length; j < jlen; j++){
33842                            tagEls[target[j]] = c;
33843                        }
33844                    }else{
33845                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33846                    }
33847                }
33848            }
33849        },
33850
33851     /**
33852      * Removes this quick tip from its element and destroys it.
33853      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33854      */
33855        unregister : function(el){
33856            delete tagEls[Roo.id(el)];
33857        },
33858
33859     /**
33860      * Enable this quick tip.
33861      */
33862        enable : function(){
33863            if(inited && disabled){
33864                locks.pop();
33865                if(locks.length < 1){
33866                    disabled = false;
33867                }
33868            }
33869        },
33870
33871     /**
33872      * Disable this quick tip.
33873      */
33874        disable : function(){
33875           disabled = true;
33876           clearTimeout(showProc);
33877           clearTimeout(hideProc);
33878           clearTimeout(dismissProc);
33879           if(ce){
33880               hide(true);
33881           }
33882           locks.push(1);
33883        },
33884
33885     /**
33886      * Returns true if the quick tip is enabled, else false.
33887      */
33888        isEnabled : function(){
33889             return !disabled;
33890        },
33891
33892         // private
33893        tagConfig : {
33894            namespace : "roo", // was ext?? this may break..
33895            alt_namespace : "ext",
33896            attribute : "qtip",
33897            width : "width",
33898            target : "target",
33899            title : "qtitle",
33900            hide : "hide",
33901            cls : "qclass"
33902        }
33903    };
33904 }();
33905
33906 // backwards compat
33907 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33908  * Based on:
33909  * Ext JS Library 1.1.1
33910  * Copyright(c) 2006-2007, Ext JS, LLC.
33911  *
33912  * Originally Released Under LGPL - original licence link has changed is not relivant.
33913  *
33914  * Fork - LGPL
33915  * <script type="text/javascript">
33916  */
33917  
33918
33919 /**
33920  * @class Roo.tree.TreePanel
33921  * @extends Roo.data.Tree
33922
33923  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33924  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33925  * @cfg {Boolean} enableDD true to enable drag and drop
33926  * @cfg {Boolean} enableDrag true to enable just drag
33927  * @cfg {Boolean} enableDrop true to enable just drop
33928  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33929  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33930  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33931  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33932  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33933  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33934  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33935  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33936  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33937  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33938  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33939  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33940  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33941  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33942  * @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>
33943  * @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>
33944  * 
33945  * @constructor
33946  * @param {String/HTMLElement/Element} el The container element
33947  * @param {Object} config
33948  */
33949 Roo.tree.TreePanel = function(el, config){
33950     var root = false;
33951     var loader = false;
33952     if (config.root) {
33953         root = config.root;
33954         delete config.root;
33955     }
33956     if (config.loader) {
33957         loader = config.loader;
33958         delete config.loader;
33959     }
33960     
33961     Roo.apply(this, config);
33962     Roo.tree.TreePanel.superclass.constructor.call(this);
33963     this.el = Roo.get(el);
33964     this.el.addClass('x-tree');
33965     //console.log(root);
33966     if (root) {
33967         this.setRootNode( Roo.factory(root, Roo.tree));
33968     }
33969     if (loader) {
33970         this.loader = Roo.factory(loader, Roo.tree);
33971     }
33972    /**
33973     * Read-only. The id of the container element becomes this TreePanel's id.
33974     */
33975     this.id = this.el.id;
33976     this.addEvents({
33977         /**
33978         * @event beforeload
33979         * Fires before a node is loaded, return false to cancel
33980         * @param {Node} node The node being loaded
33981         */
33982         "beforeload" : true,
33983         /**
33984         * @event load
33985         * Fires when a node is loaded
33986         * @param {Node} node The node that was loaded
33987         */
33988         "load" : true,
33989         /**
33990         * @event textchange
33991         * Fires when the text for a node is changed
33992         * @param {Node} node The node
33993         * @param {String} text The new text
33994         * @param {String} oldText The old text
33995         */
33996         "textchange" : true,
33997         /**
33998         * @event beforeexpand
33999         * Fires before a node is expanded, return false to cancel.
34000         * @param {Node} node The node
34001         * @param {Boolean} deep
34002         * @param {Boolean} anim
34003         */
34004         "beforeexpand" : true,
34005         /**
34006         * @event beforecollapse
34007         * Fires before a node is collapsed, return false to cancel.
34008         * @param {Node} node The node
34009         * @param {Boolean} deep
34010         * @param {Boolean} anim
34011         */
34012         "beforecollapse" : true,
34013         /**
34014         * @event expand
34015         * Fires when a node is expanded
34016         * @param {Node} node The node
34017         */
34018         "expand" : true,
34019         /**
34020         * @event disabledchange
34021         * Fires when the disabled status of a node changes
34022         * @param {Node} node The node
34023         * @param {Boolean} disabled
34024         */
34025         "disabledchange" : true,
34026         /**
34027         * @event collapse
34028         * Fires when a node is collapsed
34029         * @param {Node} node The node
34030         */
34031         "collapse" : true,
34032         /**
34033         * @event beforeclick
34034         * Fires before click processing on a node. Return false to cancel the default action.
34035         * @param {Node} node The node
34036         * @param {Roo.EventObject} e The event object
34037         */
34038         "beforeclick":true,
34039         /**
34040         * @event checkchange
34041         * Fires when a node with a checkbox's checked property changes
34042         * @param {Node} this This node
34043         * @param {Boolean} checked
34044         */
34045         "checkchange":true,
34046         /**
34047         * @event click
34048         * Fires when a node is clicked
34049         * @param {Node} node The node
34050         * @param {Roo.EventObject} e The event object
34051         */
34052         "click":true,
34053         /**
34054         * @event dblclick
34055         * Fires when a node is double clicked
34056         * @param {Node} node The node
34057         * @param {Roo.EventObject} e The event object
34058         */
34059         "dblclick":true,
34060         /**
34061         * @event contextmenu
34062         * Fires when a node is right clicked
34063         * @param {Node} node The node
34064         * @param {Roo.EventObject} e The event object
34065         */
34066         "contextmenu":true,
34067         /**
34068         * @event beforechildrenrendered
34069         * Fires right before the child nodes for a node are rendered
34070         * @param {Node} node The node
34071         */
34072         "beforechildrenrendered":true,
34073         /**
34074         * @event startdrag
34075         * Fires when a node starts being dragged
34076         * @param {Roo.tree.TreePanel} this
34077         * @param {Roo.tree.TreeNode} node
34078         * @param {event} e The raw browser event
34079         */ 
34080        "startdrag" : true,
34081        /**
34082         * @event enddrag
34083         * Fires when a drag operation is complete
34084         * @param {Roo.tree.TreePanel} this
34085         * @param {Roo.tree.TreeNode} node
34086         * @param {event} e The raw browser event
34087         */
34088        "enddrag" : true,
34089        /**
34090         * @event dragdrop
34091         * Fires when a dragged node is dropped on a valid DD target
34092         * @param {Roo.tree.TreePanel} this
34093         * @param {Roo.tree.TreeNode} node
34094         * @param {DD} dd The dd it was dropped on
34095         * @param {event} e The raw browser event
34096         */
34097        "dragdrop" : true,
34098        /**
34099         * @event beforenodedrop
34100         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34101         * passed to handlers has the following properties:<br />
34102         * <ul style="padding:5px;padding-left:16px;">
34103         * <li>tree - The TreePanel</li>
34104         * <li>target - The node being targeted for the drop</li>
34105         * <li>data - The drag data from the drag source</li>
34106         * <li>point - The point of the drop - append, above or below</li>
34107         * <li>source - The drag source</li>
34108         * <li>rawEvent - Raw mouse event</li>
34109         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34110         * to be inserted by setting them on this object.</li>
34111         * <li>cancel - Set this to true to cancel the drop.</li>
34112         * </ul>
34113         * @param {Object} dropEvent
34114         */
34115        "beforenodedrop" : true,
34116        /**
34117         * @event nodedrop
34118         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34119         * passed to handlers has the following properties:<br />
34120         * <ul style="padding:5px;padding-left:16px;">
34121         * <li>tree - The TreePanel</li>
34122         * <li>target - The node being targeted for the drop</li>
34123         * <li>data - The drag data from the drag source</li>
34124         * <li>point - The point of the drop - append, above or below</li>
34125         * <li>source - The drag source</li>
34126         * <li>rawEvent - Raw mouse event</li>
34127         * <li>dropNode - Dropped node(s).</li>
34128         * </ul>
34129         * @param {Object} dropEvent
34130         */
34131        "nodedrop" : true,
34132         /**
34133         * @event nodedragover
34134         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34135         * passed to handlers has the following properties:<br />
34136         * <ul style="padding:5px;padding-left:16px;">
34137         * <li>tree - The TreePanel</li>
34138         * <li>target - The node being targeted for the drop</li>
34139         * <li>data - The drag data from the drag source</li>
34140         * <li>point - The point of the drop - append, above or below</li>
34141         * <li>source - The drag source</li>
34142         * <li>rawEvent - Raw mouse event</li>
34143         * <li>dropNode - Drop node(s) provided by the source.</li>
34144         * <li>cancel - Set this to true to signal drop not allowed.</li>
34145         * </ul>
34146         * @param {Object} dragOverEvent
34147         */
34148        "nodedragover" : true
34149         
34150     });
34151     if(this.singleExpand){
34152        this.on("beforeexpand", this.restrictExpand, this);
34153     }
34154     if (this.editor) {
34155         this.editor.tree = this;
34156         this.editor = Roo.factory(this.editor, Roo.tree);
34157     }
34158     
34159     if (this.selModel) {
34160         this.selModel = Roo.factory(this.selModel, Roo.tree);
34161     }
34162    
34163 };
34164 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34165     rootVisible : true,
34166     animate: Roo.enableFx,
34167     lines : true,
34168     enableDD : false,
34169     hlDrop : Roo.enableFx,
34170   
34171     renderer: false,
34172     
34173     rendererTip: false,
34174     // private
34175     restrictExpand : function(node){
34176         var p = node.parentNode;
34177         if(p){
34178             if(p.expandedChild && p.expandedChild.parentNode == p){
34179                 p.expandedChild.collapse();
34180             }
34181             p.expandedChild = node;
34182         }
34183     },
34184
34185     // private override
34186     setRootNode : function(node){
34187         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34188         if(!this.rootVisible){
34189             node.ui = new Roo.tree.RootTreeNodeUI(node);
34190         }
34191         return node;
34192     },
34193
34194     /**
34195      * Returns the container element for this TreePanel
34196      */
34197     getEl : function(){
34198         return this.el;
34199     },
34200
34201     /**
34202      * Returns the default TreeLoader for this TreePanel
34203      */
34204     getLoader : function(){
34205         return this.loader;
34206     },
34207
34208     /**
34209      * Expand all nodes
34210      */
34211     expandAll : function(){
34212         this.root.expand(true);
34213     },
34214
34215     /**
34216      * Collapse all nodes
34217      */
34218     collapseAll : function(){
34219         this.root.collapse(true);
34220     },
34221
34222     /**
34223      * Returns the selection model used by this TreePanel
34224      */
34225     getSelectionModel : function(){
34226         if(!this.selModel){
34227             this.selModel = new Roo.tree.DefaultSelectionModel();
34228         }
34229         return this.selModel;
34230     },
34231
34232     /**
34233      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34234      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34235      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34236      * @return {Array}
34237      */
34238     getChecked : function(a, startNode){
34239         startNode = startNode || this.root;
34240         var r = [];
34241         var f = function(){
34242             if(this.attributes.checked){
34243                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34244             }
34245         }
34246         startNode.cascade(f);
34247         return r;
34248     },
34249
34250     /**
34251      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34252      * @param {String} path
34253      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34254      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34255      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34256      */
34257     expandPath : function(path, attr, callback){
34258         attr = attr || "id";
34259         var keys = path.split(this.pathSeparator);
34260         var curNode = this.root;
34261         if(curNode.attributes[attr] != keys[1]){ // invalid root
34262             if(callback){
34263                 callback(false, null);
34264             }
34265             return;
34266         }
34267         var index = 1;
34268         var f = function(){
34269             if(++index == keys.length){
34270                 if(callback){
34271                     callback(true, curNode);
34272                 }
34273                 return;
34274             }
34275             var c = curNode.findChild(attr, keys[index]);
34276             if(!c){
34277                 if(callback){
34278                     callback(false, curNode);
34279                 }
34280                 return;
34281             }
34282             curNode = c;
34283             c.expand(false, false, f);
34284         };
34285         curNode.expand(false, false, f);
34286     },
34287
34288     /**
34289      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34290      * @param {String} path
34291      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34292      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34293      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34294      */
34295     selectPath : function(path, attr, callback){
34296         attr = attr || "id";
34297         var keys = path.split(this.pathSeparator);
34298         var v = keys.pop();
34299         if(keys.length > 0){
34300             var f = function(success, node){
34301                 if(success && node){
34302                     var n = node.findChild(attr, v);
34303                     if(n){
34304                         n.select();
34305                         if(callback){
34306                             callback(true, n);
34307                         }
34308                     }else if(callback){
34309                         callback(false, n);
34310                     }
34311                 }else{
34312                     if(callback){
34313                         callback(false, n);
34314                     }
34315                 }
34316             };
34317             this.expandPath(keys.join(this.pathSeparator), attr, f);
34318         }else{
34319             this.root.select();
34320             if(callback){
34321                 callback(true, this.root);
34322             }
34323         }
34324     },
34325
34326     getTreeEl : function(){
34327         return this.el;
34328     },
34329
34330     /**
34331      * Trigger rendering of this TreePanel
34332      */
34333     render : function(){
34334         if (this.innerCt) {
34335             return this; // stop it rendering more than once!!
34336         }
34337         
34338         this.innerCt = this.el.createChild({tag:"ul",
34339                cls:"x-tree-root-ct " +
34340                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34341
34342         if(this.containerScroll){
34343             Roo.dd.ScrollManager.register(this.el);
34344         }
34345         if((this.enableDD || this.enableDrop) && !this.dropZone){
34346            /**
34347             * The dropZone used by this tree if drop is enabled
34348             * @type Roo.tree.TreeDropZone
34349             */
34350              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34351                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34352            });
34353         }
34354         if((this.enableDD || this.enableDrag) && !this.dragZone){
34355            /**
34356             * The dragZone used by this tree if drag is enabled
34357             * @type Roo.tree.TreeDragZone
34358             */
34359             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34360                ddGroup: this.ddGroup || "TreeDD",
34361                scroll: this.ddScroll
34362            });
34363         }
34364         this.getSelectionModel().init(this);
34365         if (!this.root) {
34366             Roo.log("ROOT not set in tree");
34367             return this;
34368         }
34369         this.root.render();
34370         if(!this.rootVisible){
34371             this.root.renderChildren();
34372         }
34373         return this;
34374     }
34375 });/*
34376  * Based on:
34377  * Ext JS Library 1.1.1
34378  * Copyright(c) 2006-2007, Ext JS, LLC.
34379  *
34380  * Originally Released Under LGPL - original licence link has changed is not relivant.
34381  *
34382  * Fork - LGPL
34383  * <script type="text/javascript">
34384  */
34385  
34386
34387 /**
34388  * @class Roo.tree.DefaultSelectionModel
34389  * @extends Roo.util.Observable
34390  * The default single selection for a TreePanel.
34391  * @param {Object} cfg Configuration
34392  */
34393 Roo.tree.DefaultSelectionModel = function(cfg){
34394    this.selNode = null;
34395    
34396    
34397    
34398    this.addEvents({
34399        /**
34400         * @event selectionchange
34401         * Fires when the selected node changes
34402         * @param {DefaultSelectionModel} this
34403         * @param {TreeNode} node the new selection
34404         */
34405        "selectionchange" : true,
34406
34407        /**
34408         * @event beforeselect
34409         * Fires before the selected node changes, return false to cancel the change
34410         * @param {DefaultSelectionModel} this
34411         * @param {TreeNode} node the new selection
34412         * @param {TreeNode} node the old selection
34413         */
34414        "beforeselect" : true
34415    });
34416    
34417     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34418 };
34419
34420 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34421     init : function(tree){
34422         this.tree = tree;
34423         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34424         tree.on("click", this.onNodeClick, this);
34425     },
34426     
34427     onNodeClick : function(node, e){
34428         if (e.ctrlKey && this.selNode == node)  {
34429             this.unselect(node);
34430             return;
34431         }
34432         this.select(node);
34433     },
34434     
34435     /**
34436      * Select a node.
34437      * @param {TreeNode} node The node to select
34438      * @return {TreeNode} The selected node
34439      */
34440     select : function(node){
34441         var last = this.selNode;
34442         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34443             if(last){
34444                 last.ui.onSelectedChange(false);
34445             }
34446             this.selNode = node;
34447             node.ui.onSelectedChange(true);
34448             this.fireEvent("selectionchange", this, node, last);
34449         }
34450         return node;
34451     },
34452     
34453     /**
34454      * Deselect a node.
34455      * @param {TreeNode} node The node to unselect
34456      */
34457     unselect : function(node){
34458         if(this.selNode == node){
34459             this.clearSelections();
34460         }    
34461     },
34462     
34463     /**
34464      * Clear all selections
34465      */
34466     clearSelections : function(){
34467         var n = this.selNode;
34468         if(n){
34469             n.ui.onSelectedChange(false);
34470             this.selNode = null;
34471             this.fireEvent("selectionchange", this, null);
34472         }
34473         return n;
34474     },
34475     
34476     /**
34477      * Get the selected node
34478      * @return {TreeNode} The selected node
34479      */
34480     getSelectedNode : function(){
34481         return this.selNode;    
34482     },
34483     
34484     /**
34485      * Returns true if the node is selected
34486      * @param {TreeNode} node The node to check
34487      * @return {Boolean}
34488      */
34489     isSelected : function(node){
34490         return this.selNode == node;  
34491     },
34492
34493     /**
34494      * Selects the node above the selected node in the tree, intelligently walking the nodes
34495      * @return TreeNode The new selection
34496      */
34497     selectPrevious : function(){
34498         var s = this.selNode || this.lastSelNode;
34499         if(!s){
34500             return null;
34501         }
34502         var ps = s.previousSibling;
34503         if(ps){
34504             if(!ps.isExpanded() || ps.childNodes.length < 1){
34505                 return this.select(ps);
34506             } else{
34507                 var lc = ps.lastChild;
34508                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34509                     lc = lc.lastChild;
34510                 }
34511                 return this.select(lc);
34512             }
34513         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34514             return this.select(s.parentNode);
34515         }
34516         return null;
34517     },
34518
34519     /**
34520      * Selects the node above the selected node in the tree, intelligently walking the nodes
34521      * @return TreeNode The new selection
34522      */
34523     selectNext : function(){
34524         var s = this.selNode || this.lastSelNode;
34525         if(!s){
34526             return null;
34527         }
34528         if(s.firstChild && s.isExpanded()){
34529              return this.select(s.firstChild);
34530          }else if(s.nextSibling){
34531              return this.select(s.nextSibling);
34532          }else if(s.parentNode){
34533             var newS = null;
34534             s.parentNode.bubble(function(){
34535                 if(this.nextSibling){
34536                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34537                     return false;
34538                 }
34539             });
34540             return newS;
34541          }
34542         return null;
34543     },
34544
34545     onKeyDown : function(e){
34546         var s = this.selNode || this.lastSelNode;
34547         // undesirable, but required
34548         var sm = this;
34549         if(!s){
34550             return;
34551         }
34552         var k = e.getKey();
34553         switch(k){
34554              case e.DOWN:
34555                  e.stopEvent();
34556                  this.selectNext();
34557              break;
34558              case e.UP:
34559                  e.stopEvent();
34560                  this.selectPrevious();
34561              break;
34562              case e.RIGHT:
34563                  e.preventDefault();
34564                  if(s.hasChildNodes()){
34565                      if(!s.isExpanded()){
34566                          s.expand();
34567                      }else if(s.firstChild){
34568                          this.select(s.firstChild, e);
34569                      }
34570                  }
34571              break;
34572              case e.LEFT:
34573                  e.preventDefault();
34574                  if(s.hasChildNodes() && s.isExpanded()){
34575                      s.collapse();
34576                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34577                      this.select(s.parentNode, e);
34578                  }
34579              break;
34580         };
34581     }
34582 });
34583
34584 /**
34585  * @class Roo.tree.MultiSelectionModel
34586  * @extends Roo.util.Observable
34587  * Multi selection for a TreePanel.
34588  * @param {Object} cfg Configuration
34589  */
34590 Roo.tree.MultiSelectionModel = function(){
34591    this.selNodes = [];
34592    this.selMap = {};
34593    this.addEvents({
34594        /**
34595         * @event selectionchange
34596         * Fires when the selected nodes change
34597         * @param {MultiSelectionModel} this
34598         * @param {Array} nodes Array of the selected nodes
34599         */
34600        "selectionchange" : true
34601    });
34602    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34603    
34604 };
34605
34606 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34607     init : function(tree){
34608         this.tree = tree;
34609         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34610         tree.on("click", this.onNodeClick, this);
34611     },
34612     
34613     onNodeClick : function(node, e){
34614         this.select(node, e, e.ctrlKey);
34615     },
34616     
34617     /**
34618      * Select a node.
34619      * @param {TreeNode} node The node to select
34620      * @param {EventObject} e (optional) An event associated with the selection
34621      * @param {Boolean} keepExisting True to retain existing selections
34622      * @return {TreeNode} The selected node
34623      */
34624     select : function(node, e, keepExisting){
34625         if(keepExisting !== true){
34626             this.clearSelections(true);
34627         }
34628         if(this.isSelected(node)){
34629             this.lastSelNode = node;
34630             return node;
34631         }
34632         this.selNodes.push(node);
34633         this.selMap[node.id] = node;
34634         this.lastSelNode = node;
34635         node.ui.onSelectedChange(true);
34636         this.fireEvent("selectionchange", this, this.selNodes);
34637         return node;
34638     },
34639     
34640     /**
34641      * Deselect a node.
34642      * @param {TreeNode} node The node to unselect
34643      */
34644     unselect : function(node){
34645         if(this.selMap[node.id]){
34646             node.ui.onSelectedChange(false);
34647             var sn = this.selNodes;
34648             var index = -1;
34649             if(sn.indexOf){
34650                 index = sn.indexOf(node);
34651             }else{
34652                 for(var i = 0, len = sn.length; i < len; i++){
34653                     if(sn[i] == node){
34654                         index = i;
34655                         break;
34656                     }
34657                 }
34658             }
34659             if(index != -1){
34660                 this.selNodes.splice(index, 1);
34661             }
34662             delete this.selMap[node.id];
34663             this.fireEvent("selectionchange", this, this.selNodes);
34664         }
34665     },
34666     
34667     /**
34668      * Clear all selections
34669      */
34670     clearSelections : function(suppressEvent){
34671         var sn = this.selNodes;
34672         if(sn.length > 0){
34673             for(var i = 0, len = sn.length; i < len; i++){
34674                 sn[i].ui.onSelectedChange(false);
34675             }
34676             this.selNodes = [];
34677             this.selMap = {};
34678             if(suppressEvent !== true){
34679                 this.fireEvent("selectionchange", this, this.selNodes);
34680             }
34681         }
34682     },
34683     
34684     /**
34685      * Returns true if the node is selected
34686      * @param {TreeNode} node The node to check
34687      * @return {Boolean}
34688      */
34689     isSelected : function(node){
34690         return this.selMap[node.id] ? true : false;  
34691     },
34692     
34693     /**
34694      * Returns an array of the selected nodes
34695      * @return {Array}
34696      */
34697     getSelectedNodes : function(){
34698         return this.selNodes;    
34699     },
34700
34701     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34702
34703     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34704
34705     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34706 });/*
34707  * Based on:
34708  * Ext JS Library 1.1.1
34709  * Copyright(c) 2006-2007, Ext JS, LLC.
34710  *
34711  * Originally Released Under LGPL - original licence link has changed is not relivant.
34712  *
34713  * Fork - LGPL
34714  * <script type="text/javascript">
34715  */
34716  
34717 /**
34718  * @class Roo.tree.TreeNode
34719  * @extends Roo.data.Node
34720  * @cfg {String} text The text for this node
34721  * @cfg {Boolean} expanded true to start the node expanded
34722  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34723  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34724  * @cfg {Boolean} disabled true to start the node disabled
34725  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34726  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34727  * @cfg {String} cls A css class to be added to the node
34728  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34729  * @cfg {String} href URL of the link used for the node (defaults to #)
34730  * @cfg {String} hrefTarget target frame for the link
34731  * @cfg {String} qtip An Ext QuickTip for the node
34732  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34733  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34734  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34735  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34736  * (defaults to undefined with no checkbox rendered)
34737  * @constructor
34738  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34739  */
34740 Roo.tree.TreeNode = function(attributes){
34741     attributes = attributes || {};
34742     if(typeof attributes == "string"){
34743         attributes = {text: attributes};
34744     }
34745     this.childrenRendered = false;
34746     this.rendered = false;
34747     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34748     this.expanded = attributes.expanded === true;
34749     this.isTarget = attributes.isTarget !== false;
34750     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34751     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34752
34753     /**
34754      * Read-only. The text for this node. To change it use setText().
34755      * @type String
34756      */
34757     this.text = attributes.text;
34758     /**
34759      * True if this node is disabled.
34760      * @type Boolean
34761      */
34762     this.disabled = attributes.disabled === true;
34763
34764     this.addEvents({
34765         /**
34766         * @event textchange
34767         * Fires when the text for this node is changed
34768         * @param {Node} this This node
34769         * @param {String} text The new text
34770         * @param {String} oldText The old text
34771         */
34772         "textchange" : true,
34773         /**
34774         * @event beforeexpand
34775         * Fires before this node is expanded, return false to cancel.
34776         * @param {Node} this This node
34777         * @param {Boolean} deep
34778         * @param {Boolean} anim
34779         */
34780         "beforeexpand" : true,
34781         /**
34782         * @event beforecollapse
34783         * Fires before this node is collapsed, return false to cancel.
34784         * @param {Node} this This node
34785         * @param {Boolean} deep
34786         * @param {Boolean} anim
34787         */
34788         "beforecollapse" : true,
34789         /**
34790         * @event expand
34791         * Fires when this node is expanded
34792         * @param {Node} this This node
34793         */
34794         "expand" : true,
34795         /**
34796         * @event disabledchange
34797         * Fires when the disabled status of this node changes
34798         * @param {Node} this This node
34799         * @param {Boolean} disabled
34800         */
34801         "disabledchange" : true,
34802         /**
34803         * @event collapse
34804         * Fires when this node is collapsed
34805         * @param {Node} this This node
34806         */
34807         "collapse" : true,
34808         /**
34809         * @event beforeclick
34810         * Fires before click processing. Return false to cancel the default action.
34811         * @param {Node} this This node
34812         * @param {Roo.EventObject} e The event object
34813         */
34814         "beforeclick":true,
34815         /**
34816         * @event checkchange
34817         * Fires when a node with a checkbox's checked property changes
34818         * @param {Node} this This node
34819         * @param {Boolean} checked
34820         */
34821         "checkchange":true,
34822         /**
34823         * @event click
34824         * Fires when this node is clicked
34825         * @param {Node} this This node
34826         * @param {Roo.EventObject} e The event object
34827         */
34828         "click":true,
34829         /**
34830         * @event dblclick
34831         * Fires when this node is double clicked
34832         * @param {Node} this This node
34833         * @param {Roo.EventObject} e The event object
34834         */
34835         "dblclick":true,
34836         /**
34837         * @event contextmenu
34838         * Fires when this node is right clicked
34839         * @param {Node} this This node
34840         * @param {Roo.EventObject} e The event object
34841         */
34842         "contextmenu":true,
34843         /**
34844         * @event beforechildrenrendered
34845         * Fires right before the child nodes for this node are rendered
34846         * @param {Node} this This node
34847         */
34848         "beforechildrenrendered":true
34849     });
34850
34851     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34852
34853     /**
34854      * Read-only. The UI for this node
34855      * @type TreeNodeUI
34856      */
34857     this.ui = new uiClass(this);
34858     
34859     // finally support items[]
34860     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34861         return;
34862     }
34863     
34864     
34865     Roo.each(this.attributes.items, function(c) {
34866         this.appendChild(Roo.factory(c,Roo.Tree));
34867     }, this);
34868     delete this.attributes.items;
34869     
34870     
34871     
34872 };
34873 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34874     preventHScroll: true,
34875     /**
34876      * Returns true if this node is expanded
34877      * @return {Boolean}
34878      */
34879     isExpanded : function(){
34880         return this.expanded;
34881     },
34882
34883     /**
34884      * Returns the UI object for this node
34885      * @return {TreeNodeUI}
34886      */
34887     getUI : function(){
34888         return this.ui;
34889     },
34890
34891     // private override
34892     setFirstChild : function(node){
34893         var of = this.firstChild;
34894         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34895         if(this.childrenRendered && of && node != of){
34896             of.renderIndent(true, true);
34897         }
34898         if(this.rendered){
34899             this.renderIndent(true, true);
34900         }
34901     },
34902
34903     // private override
34904     setLastChild : function(node){
34905         var ol = this.lastChild;
34906         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34907         if(this.childrenRendered && ol && node != ol){
34908             ol.renderIndent(true, true);
34909         }
34910         if(this.rendered){
34911             this.renderIndent(true, true);
34912         }
34913     },
34914
34915     // these methods are overridden to provide lazy rendering support
34916     // private override
34917     appendChild : function()
34918     {
34919         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34920         if(node && this.childrenRendered){
34921             node.render();
34922         }
34923         this.ui.updateExpandIcon();
34924         return node;
34925     },
34926
34927     // private override
34928     removeChild : function(node){
34929         this.ownerTree.getSelectionModel().unselect(node);
34930         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34931         // if it's been rendered remove dom node
34932         if(this.childrenRendered){
34933             node.ui.remove();
34934         }
34935         if(this.childNodes.length < 1){
34936             this.collapse(false, false);
34937         }else{
34938             this.ui.updateExpandIcon();
34939         }
34940         if(!this.firstChild) {
34941             this.childrenRendered = false;
34942         }
34943         return node;
34944     },
34945
34946     // private override
34947     insertBefore : function(node, refNode){
34948         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34949         if(newNode && refNode && this.childrenRendered){
34950             node.render();
34951         }
34952         this.ui.updateExpandIcon();
34953         return newNode;
34954     },
34955
34956     /**
34957      * Sets the text for this node
34958      * @param {String} text
34959      */
34960     setText : function(text){
34961         var oldText = this.text;
34962         this.text = text;
34963         this.attributes.text = text;
34964         if(this.rendered){ // event without subscribing
34965             this.ui.onTextChange(this, text, oldText);
34966         }
34967         this.fireEvent("textchange", this, text, oldText);
34968     },
34969
34970     /**
34971      * Triggers selection of this node
34972      */
34973     select : function(){
34974         this.getOwnerTree().getSelectionModel().select(this);
34975     },
34976
34977     /**
34978      * Triggers deselection of this node
34979      */
34980     unselect : function(){
34981         this.getOwnerTree().getSelectionModel().unselect(this);
34982     },
34983
34984     /**
34985      * Returns true if this node is selected
34986      * @return {Boolean}
34987      */
34988     isSelected : function(){
34989         return this.getOwnerTree().getSelectionModel().isSelected(this);
34990     },
34991
34992     /**
34993      * Expand this node.
34994      * @param {Boolean} deep (optional) True to expand all children as well
34995      * @param {Boolean} anim (optional) false to cancel the default animation
34996      * @param {Function} callback (optional) A callback to be called when
34997      * expanding this node completes (does not wait for deep expand to complete).
34998      * Called with 1 parameter, this node.
34999      */
35000     expand : function(deep, anim, callback){
35001         if(!this.expanded){
35002             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35003                 return;
35004             }
35005             if(!this.childrenRendered){
35006                 this.renderChildren();
35007             }
35008             this.expanded = true;
35009             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35010                 this.ui.animExpand(function(){
35011                     this.fireEvent("expand", this);
35012                     if(typeof callback == "function"){
35013                         callback(this);
35014                     }
35015                     if(deep === true){
35016                         this.expandChildNodes(true);
35017                     }
35018                 }.createDelegate(this));
35019                 return;
35020             }else{
35021                 this.ui.expand();
35022                 this.fireEvent("expand", this);
35023                 if(typeof callback == "function"){
35024                     callback(this);
35025                 }
35026             }
35027         }else{
35028            if(typeof callback == "function"){
35029                callback(this);
35030            }
35031         }
35032         if(deep === true){
35033             this.expandChildNodes(true);
35034         }
35035     },
35036
35037     isHiddenRoot : function(){
35038         return this.isRoot && !this.getOwnerTree().rootVisible;
35039     },
35040
35041     /**
35042      * Collapse this node.
35043      * @param {Boolean} deep (optional) True to collapse all children as well
35044      * @param {Boolean} anim (optional) false to cancel the default animation
35045      */
35046     collapse : function(deep, anim){
35047         if(this.expanded && !this.isHiddenRoot()){
35048             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35049                 return;
35050             }
35051             this.expanded = false;
35052             if((this.getOwnerTree().animate && anim !== false) || anim){
35053                 this.ui.animCollapse(function(){
35054                     this.fireEvent("collapse", this);
35055                     if(deep === true){
35056                         this.collapseChildNodes(true);
35057                     }
35058                 }.createDelegate(this));
35059                 return;
35060             }else{
35061                 this.ui.collapse();
35062                 this.fireEvent("collapse", this);
35063             }
35064         }
35065         if(deep === true){
35066             var cs = this.childNodes;
35067             for(var i = 0, len = cs.length; i < len; i++) {
35068                 cs[i].collapse(true, false);
35069             }
35070         }
35071     },
35072
35073     // private
35074     delayedExpand : function(delay){
35075         if(!this.expandProcId){
35076             this.expandProcId = this.expand.defer(delay, this);
35077         }
35078     },
35079
35080     // private
35081     cancelExpand : function(){
35082         if(this.expandProcId){
35083             clearTimeout(this.expandProcId);
35084         }
35085         this.expandProcId = false;
35086     },
35087
35088     /**
35089      * Toggles expanded/collapsed state of the node
35090      */
35091     toggle : function(){
35092         if(this.expanded){
35093             this.collapse();
35094         }else{
35095             this.expand();
35096         }
35097     },
35098
35099     /**
35100      * Ensures all parent nodes are expanded
35101      */
35102     ensureVisible : function(callback){
35103         var tree = this.getOwnerTree();
35104         tree.expandPath(this.parentNode.getPath(), false, function(){
35105             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35106             Roo.callback(callback);
35107         }.createDelegate(this));
35108     },
35109
35110     /**
35111      * Expand all child nodes
35112      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35113      */
35114     expandChildNodes : function(deep){
35115         var cs = this.childNodes;
35116         for(var i = 0, len = cs.length; i < len; i++) {
35117                 cs[i].expand(deep);
35118         }
35119     },
35120
35121     /**
35122      * Collapse all child nodes
35123      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35124      */
35125     collapseChildNodes : function(deep){
35126         var cs = this.childNodes;
35127         for(var i = 0, len = cs.length; i < len; i++) {
35128                 cs[i].collapse(deep);
35129         }
35130     },
35131
35132     /**
35133      * Disables this node
35134      */
35135     disable : function(){
35136         this.disabled = true;
35137         this.unselect();
35138         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35139             this.ui.onDisableChange(this, true);
35140         }
35141         this.fireEvent("disabledchange", this, true);
35142     },
35143
35144     /**
35145      * Enables this node
35146      */
35147     enable : function(){
35148         this.disabled = false;
35149         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35150             this.ui.onDisableChange(this, false);
35151         }
35152         this.fireEvent("disabledchange", this, false);
35153     },
35154
35155     // private
35156     renderChildren : function(suppressEvent){
35157         if(suppressEvent !== false){
35158             this.fireEvent("beforechildrenrendered", this);
35159         }
35160         var cs = this.childNodes;
35161         for(var i = 0, len = cs.length; i < len; i++){
35162             cs[i].render(true);
35163         }
35164         this.childrenRendered = true;
35165     },
35166
35167     // private
35168     sort : function(fn, scope){
35169         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35170         if(this.childrenRendered){
35171             var cs = this.childNodes;
35172             for(var i = 0, len = cs.length; i < len; i++){
35173                 cs[i].render(true);
35174             }
35175         }
35176     },
35177
35178     // private
35179     render : function(bulkRender){
35180         this.ui.render(bulkRender);
35181         if(!this.rendered){
35182             this.rendered = true;
35183             if(this.expanded){
35184                 this.expanded = false;
35185                 this.expand(false, false);
35186             }
35187         }
35188     },
35189
35190     // private
35191     renderIndent : function(deep, refresh){
35192         if(refresh){
35193             this.ui.childIndent = null;
35194         }
35195         this.ui.renderIndent();
35196         if(deep === true && this.childrenRendered){
35197             var cs = this.childNodes;
35198             for(var i = 0, len = cs.length; i < len; i++){
35199                 cs[i].renderIndent(true, refresh);
35200             }
35201         }
35202     }
35203 });/*
35204  * Based on:
35205  * Ext JS Library 1.1.1
35206  * Copyright(c) 2006-2007, Ext JS, LLC.
35207  *
35208  * Originally Released Under LGPL - original licence link has changed is not relivant.
35209  *
35210  * Fork - LGPL
35211  * <script type="text/javascript">
35212  */
35213  
35214 /**
35215  * @class Roo.tree.AsyncTreeNode
35216  * @extends Roo.tree.TreeNode
35217  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35218  * @constructor
35219  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35220  */
35221  Roo.tree.AsyncTreeNode = function(config){
35222     this.loaded = false;
35223     this.loading = false;
35224     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35225     /**
35226     * @event beforeload
35227     * Fires before this node is loaded, return false to cancel
35228     * @param {Node} this This node
35229     */
35230     this.addEvents({'beforeload':true, 'load': true});
35231     /**
35232     * @event load
35233     * Fires when this node is loaded
35234     * @param {Node} this This node
35235     */
35236     /**
35237      * The loader used by this node (defaults to using the tree's defined loader)
35238      * @type TreeLoader
35239      * @property loader
35240      */
35241 };
35242 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35243     expand : function(deep, anim, callback){
35244         if(this.loading){ // if an async load is already running, waiting til it's done
35245             var timer;
35246             var f = function(){
35247                 if(!this.loading){ // done loading
35248                     clearInterval(timer);
35249                     this.expand(deep, anim, callback);
35250                 }
35251             }.createDelegate(this);
35252             timer = setInterval(f, 200);
35253             return;
35254         }
35255         if(!this.loaded){
35256             if(this.fireEvent("beforeload", this) === false){
35257                 return;
35258             }
35259             this.loading = true;
35260             this.ui.beforeLoad(this);
35261             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35262             if(loader){
35263                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35264                 return;
35265             }
35266         }
35267         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35268     },
35269     
35270     /**
35271      * Returns true if this node is currently loading
35272      * @return {Boolean}
35273      */
35274     isLoading : function(){
35275         return this.loading;  
35276     },
35277     
35278     loadComplete : function(deep, anim, callback){
35279         this.loading = false;
35280         this.loaded = true;
35281         this.ui.afterLoad(this);
35282         this.fireEvent("load", this);
35283         this.expand(deep, anim, callback);
35284     },
35285     
35286     /**
35287      * Returns true if this node has been loaded
35288      * @return {Boolean}
35289      */
35290     isLoaded : function(){
35291         return this.loaded;
35292     },
35293     
35294     hasChildNodes : function(){
35295         if(!this.isLeaf() && !this.loaded){
35296             return true;
35297         }else{
35298             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35299         }
35300     },
35301
35302     /**
35303      * Trigger a reload for this node
35304      * @param {Function} callback
35305      */
35306     reload : function(callback){
35307         this.collapse(false, false);
35308         while(this.firstChild){
35309             this.removeChild(this.firstChild);
35310         }
35311         this.childrenRendered = false;
35312         this.loaded = false;
35313         if(this.isHiddenRoot()){
35314             this.expanded = false;
35315         }
35316         this.expand(false, false, callback);
35317     }
35318 });/*
35319  * Based on:
35320  * Ext JS Library 1.1.1
35321  * Copyright(c) 2006-2007, Ext JS, LLC.
35322  *
35323  * Originally Released Under LGPL - original licence link has changed is not relivant.
35324  *
35325  * Fork - LGPL
35326  * <script type="text/javascript">
35327  */
35328  
35329 /**
35330  * @class Roo.tree.TreeNodeUI
35331  * @constructor
35332  * @param {Object} node The node to render
35333  * The TreeNode UI implementation is separate from the
35334  * tree implementation. Unless you are customizing the tree UI,
35335  * you should never have to use this directly.
35336  */
35337 Roo.tree.TreeNodeUI = function(node){
35338     this.node = node;
35339     this.rendered = false;
35340     this.animating = false;
35341     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35342 };
35343
35344 Roo.tree.TreeNodeUI.prototype = {
35345     removeChild : function(node){
35346         if(this.rendered){
35347             this.ctNode.removeChild(node.ui.getEl());
35348         }
35349     },
35350
35351     beforeLoad : function(){
35352          this.addClass("x-tree-node-loading");
35353     },
35354
35355     afterLoad : function(){
35356          this.removeClass("x-tree-node-loading");
35357     },
35358
35359     onTextChange : function(node, text, oldText){
35360         if(this.rendered){
35361             this.textNode.innerHTML = text;
35362         }
35363     },
35364
35365     onDisableChange : function(node, state){
35366         this.disabled = state;
35367         if(state){
35368             this.addClass("x-tree-node-disabled");
35369         }else{
35370             this.removeClass("x-tree-node-disabled");
35371         }
35372     },
35373
35374     onSelectedChange : function(state){
35375         if(state){
35376             this.focus();
35377             this.addClass("x-tree-selected");
35378         }else{
35379             //this.blur();
35380             this.removeClass("x-tree-selected");
35381         }
35382     },
35383
35384     onMove : function(tree, node, oldParent, newParent, index, refNode){
35385         this.childIndent = null;
35386         if(this.rendered){
35387             var targetNode = newParent.ui.getContainer();
35388             if(!targetNode){//target not rendered
35389                 this.holder = document.createElement("div");
35390                 this.holder.appendChild(this.wrap);
35391                 return;
35392             }
35393             var insertBefore = refNode ? refNode.ui.getEl() : null;
35394             if(insertBefore){
35395                 targetNode.insertBefore(this.wrap, insertBefore);
35396             }else{
35397                 targetNode.appendChild(this.wrap);
35398             }
35399             this.node.renderIndent(true);
35400         }
35401     },
35402
35403     addClass : function(cls){
35404         if(this.elNode){
35405             Roo.fly(this.elNode).addClass(cls);
35406         }
35407     },
35408
35409     removeClass : function(cls){
35410         if(this.elNode){
35411             Roo.fly(this.elNode).removeClass(cls);
35412         }
35413     },
35414
35415     remove : function(){
35416         if(this.rendered){
35417             this.holder = document.createElement("div");
35418             this.holder.appendChild(this.wrap);
35419         }
35420     },
35421
35422     fireEvent : function(){
35423         return this.node.fireEvent.apply(this.node, arguments);
35424     },
35425
35426     initEvents : function(){
35427         this.node.on("move", this.onMove, this);
35428         var E = Roo.EventManager;
35429         var a = this.anchor;
35430
35431         var el = Roo.fly(a, '_treeui');
35432
35433         if(Roo.isOpera){ // opera render bug ignores the CSS
35434             el.setStyle("text-decoration", "none");
35435         }
35436
35437         el.on("click", this.onClick, this);
35438         el.on("dblclick", this.onDblClick, this);
35439
35440         if(this.checkbox){
35441             Roo.EventManager.on(this.checkbox,
35442                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35443         }
35444
35445         el.on("contextmenu", this.onContextMenu, this);
35446
35447         var icon = Roo.fly(this.iconNode);
35448         icon.on("click", this.onClick, this);
35449         icon.on("dblclick", this.onDblClick, this);
35450         icon.on("contextmenu", this.onContextMenu, this);
35451         E.on(this.ecNode, "click", this.ecClick, this, true);
35452
35453         if(this.node.disabled){
35454             this.addClass("x-tree-node-disabled");
35455         }
35456         if(this.node.hidden){
35457             this.addClass("x-tree-node-disabled");
35458         }
35459         var ot = this.node.getOwnerTree();
35460         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35461         if(dd && (!this.node.isRoot || ot.rootVisible)){
35462             Roo.dd.Registry.register(this.elNode, {
35463                 node: this.node,
35464                 handles: this.getDDHandles(),
35465                 isHandle: false
35466             });
35467         }
35468     },
35469
35470     getDDHandles : function(){
35471         return [this.iconNode, this.textNode];
35472     },
35473
35474     hide : function(){
35475         if(this.rendered){
35476             this.wrap.style.display = "none";
35477         }
35478     },
35479
35480     show : function(){
35481         if(this.rendered){
35482             this.wrap.style.display = "";
35483         }
35484     },
35485
35486     onContextMenu : function(e){
35487         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35488             e.preventDefault();
35489             this.focus();
35490             this.fireEvent("contextmenu", this.node, e);
35491         }
35492     },
35493
35494     onClick : function(e){
35495         if(this.dropping){
35496             e.stopEvent();
35497             return;
35498         }
35499         if(this.fireEvent("beforeclick", this.node, e) !== false){
35500             if(!this.disabled && this.node.attributes.href){
35501                 this.fireEvent("click", this.node, e);
35502                 return;
35503             }
35504             e.preventDefault();
35505             if(this.disabled){
35506                 return;
35507             }
35508
35509             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35510                 this.node.toggle();
35511             }
35512
35513             this.fireEvent("click", this.node, e);
35514         }else{
35515             e.stopEvent();
35516         }
35517     },
35518
35519     onDblClick : function(e){
35520         e.preventDefault();
35521         if(this.disabled){
35522             return;
35523         }
35524         if(this.checkbox){
35525             this.toggleCheck();
35526         }
35527         if(!this.animating && this.node.hasChildNodes()){
35528             this.node.toggle();
35529         }
35530         this.fireEvent("dblclick", this.node, e);
35531     },
35532
35533     onCheckChange : function(){
35534         var checked = this.checkbox.checked;
35535         this.node.attributes.checked = checked;
35536         this.fireEvent('checkchange', this.node, checked);
35537     },
35538
35539     ecClick : function(e){
35540         if(!this.animating && this.node.hasChildNodes()){
35541             this.node.toggle();
35542         }
35543     },
35544
35545     startDrop : function(){
35546         this.dropping = true;
35547     },
35548
35549     // delayed drop so the click event doesn't get fired on a drop
35550     endDrop : function(){
35551        setTimeout(function(){
35552            this.dropping = false;
35553        }.createDelegate(this), 50);
35554     },
35555
35556     expand : function(){
35557         this.updateExpandIcon();
35558         this.ctNode.style.display = "";
35559     },
35560
35561     focus : function(){
35562         if(!this.node.preventHScroll){
35563             try{this.anchor.focus();
35564             }catch(e){}
35565         }else if(!Roo.isIE){
35566             try{
35567                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35568                 var l = noscroll.scrollLeft;
35569                 this.anchor.focus();
35570                 noscroll.scrollLeft = l;
35571             }catch(e){}
35572         }
35573     },
35574
35575     toggleCheck : function(value){
35576         var cb = this.checkbox;
35577         if(cb){
35578             cb.checked = (value === undefined ? !cb.checked : value);
35579         }
35580     },
35581
35582     blur : function(){
35583         try{
35584             this.anchor.blur();
35585         }catch(e){}
35586     },
35587
35588     animExpand : function(callback){
35589         var ct = Roo.get(this.ctNode);
35590         ct.stopFx();
35591         if(!this.node.hasChildNodes()){
35592             this.updateExpandIcon();
35593             this.ctNode.style.display = "";
35594             Roo.callback(callback);
35595             return;
35596         }
35597         this.animating = true;
35598         this.updateExpandIcon();
35599
35600         ct.slideIn('t', {
35601            callback : function(){
35602                this.animating = false;
35603                Roo.callback(callback);
35604             },
35605             scope: this,
35606             duration: this.node.ownerTree.duration || .25
35607         });
35608     },
35609
35610     highlight : function(){
35611         var tree = this.node.getOwnerTree();
35612         Roo.fly(this.wrap).highlight(
35613             tree.hlColor || "C3DAF9",
35614             {endColor: tree.hlBaseColor}
35615         );
35616     },
35617
35618     collapse : function(){
35619         this.updateExpandIcon();
35620         this.ctNode.style.display = "none";
35621     },
35622
35623     animCollapse : function(callback){
35624         var ct = Roo.get(this.ctNode);
35625         ct.enableDisplayMode('block');
35626         ct.stopFx();
35627
35628         this.animating = true;
35629         this.updateExpandIcon();
35630
35631         ct.slideOut('t', {
35632             callback : function(){
35633                this.animating = false;
35634                Roo.callback(callback);
35635             },
35636             scope: this,
35637             duration: this.node.ownerTree.duration || .25
35638         });
35639     },
35640
35641     getContainer : function(){
35642         return this.ctNode;
35643     },
35644
35645     getEl : function(){
35646         return this.wrap;
35647     },
35648
35649     appendDDGhost : function(ghostNode){
35650         ghostNode.appendChild(this.elNode.cloneNode(true));
35651     },
35652
35653     getDDRepairXY : function(){
35654         return Roo.lib.Dom.getXY(this.iconNode);
35655     },
35656
35657     onRender : function(){
35658         this.render();
35659     },
35660
35661     render : function(bulkRender){
35662         var n = this.node, a = n.attributes;
35663         var targetNode = n.parentNode ?
35664               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35665
35666         if(!this.rendered){
35667             this.rendered = true;
35668
35669             this.renderElements(n, a, targetNode, bulkRender);
35670
35671             if(a.qtip){
35672                if(this.textNode.setAttributeNS){
35673                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35674                    if(a.qtipTitle){
35675                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35676                    }
35677                }else{
35678                    this.textNode.setAttribute("ext:qtip", a.qtip);
35679                    if(a.qtipTitle){
35680                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35681                    }
35682                }
35683             }else if(a.qtipCfg){
35684                 a.qtipCfg.target = Roo.id(this.textNode);
35685                 Roo.QuickTips.register(a.qtipCfg);
35686             }
35687             this.initEvents();
35688             if(!this.node.expanded){
35689                 this.updateExpandIcon();
35690             }
35691         }else{
35692             if(bulkRender === true) {
35693                 targetNode.appendChild(this.wrap);
35694             }
35695         }
35696     },
35697
35698     renderElements : function(n, a, targetNode, bulkRender)
35699     {
35700         // add some indent caching, this helps performance when rendering a large tree
35701         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35702         var t = n.getOwnerTree();
35703         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35704         if (typeof(n.attributes.html) != 'undefined') {
35705             txt = n.attributes.html;
35706         }
35707         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35708         var cb = typeof a.checked == 'boolean';
35709         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35710         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35711             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35712             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35713             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35714             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35715             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35716              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35717                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35718             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35719             "</li>"];
35720
35721         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35722             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35723                                 n.nextSibling.ui.getEl(), buf.join(""));
35724         }else{
35725             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35726         }
35727
35728         this.elNode = this.wrap.childNodes[0];
35729         this.ctNode = this.wrap.childNodes[1];
35730         var cs = this.elNode.childNodes;
35731         this.indentNode = cs[0];
35732         this.ecNode = cs[1];
35733         this.iconNode = cs[2];
35734         var index = 3;
35735         if(cb){
35736             this.checkbox = cs[3];
35737             index++;
35738         }
35739         this.anchor = cs[index];
35740         this.textNode = cs[index].firstChild;
35741     },
35742
35743     getAnchor : function(){
35744         return this.anchor;
35745     },
35746
35747     getTextEl : function(){
35748         return this.textNode;
35749     },
35750
35751     getIconEl : function(){
35752         return this.iconNode;
35753     },
35754
35755     isChecked : function(){
35756         return this.checkbox ? this.checkbox.checked : false;
35757     },
35758
35759     updateExpandIcon : function(){
35760         if(this.rendered){
35761             var n = this.node, c1, c2;
35762             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35763             var hasChild = n.hasChildNodes();
35764             if(hasChild){
35765                 if(n.expanded){
35766                     cls += "-minus";
35767                     c1 = "x-tree-node-collapsed";
35768                     c2 = "x-tree-node-expanded";
35769                 }else{
35770                     cls += "-plus";
35771                     c1 = "x-tree-node-expanded";
35772                     c2 = "x-tree-node-collapsed";
35773                 }
35774                 if(this.wasLeaf){
35775                     this.removeClass("x-tree-node-leaf");
35776                     this.wasLeaf = false;
35777                 }
35778                 if(this.c1 != c1 || this.c2 != c2){
35779                     Roo.fly(this.elNode).replaceClass(c1, c2);
35780                     this.c1 = c1; this.c2 = c2;
35781                 }
35782             }else{
35783                 // this changes non-leafs into leafs if they have no children.
35784                 // it's not very rational behaviour..
35785                 
35786                 if(!this.wasLeaf && this.node.leaf){
35787                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35788                     delete this.c1;
35789                     delete this.c2;
35790                     this.wasLeaf = true;
35791                 }
35792             }
35793             var ecc = "x-tree-ec-icon "+cls;
35794             if(this.ecc != ecc){
35795                 this.ecNode.className = ecc;
35796                 this.ecc = ecc;
35797             }
35798         }
35799     },
35800
35801     getChildIndent : function(){
35802         if(!this.childIndent){
35803             var buf = [];
35804             var p = this.node;
35805             while(p){
35806                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35807                     if(!p.isLast()) {
35808                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35809                     } else {
35810                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35811                     }
35812                 }
35813                 p = p.parentNode;
35814             }
35815             this.childIndent = buf.join("");
35816         }
35817         return this.childIndent;
35818     },
35819
35820     renderIndent : function(){
35821         if(this.rendered){
35822             var indent = "";
35823             var p = this.node.parentNode;
35824             if(p){
35825                 indent = p.ui.getChildIndent();
35826             }
35827             if(this.indentMarkup != indent){ // don't rerender if not required
35828                 this.indentNode.innerHTML = indent;
35829                 this.indentMarkup = indent;
35830             }
35831             this.updateExpandIcon();
35832         }
35833     }
35834 };
35835
35836 Roo.tree.RootTreeNodeUI = function(){
35837     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35838 };
35839 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35840     render : function(){
35841         if(!this.rendered){
35842             var targetNode = this.node.ownerTree.innerCt.dom;
35843             this.node.expanded = true;
35844             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35845             this.wrap = this.ctNode = targetNode.firstChild;
35846         }
35847     },
35848     collapse : function(){
35849     },
35850     expand : function(){
35851     }
35852 });/*
35853  * Based on:
35854  * Ext JS Library 1.1.1
35855  * Copyright(c) 2006-2007, Ext JS, LLC.
35856  *
35857  * Originally Released Under LGPL - original licence link has changed is not relivant.
35858  *
35859  * Fork - LGPL
35860  * <script type="text/javascript">
35861  */
35862 /**
35863  * @class Roo.tree.TreeLoader
35864  * @extends Roo.util.Observable
35865  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35866  * nodes from a specified URL. The response must be a javascript Array definition
35867  * who's elements are node definition objects. eg:
35868  * <pre><code>
35869 {  success : true,
35870    data :      [
35871    
35872     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35873     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35874     ]
35875 }
35876
35877
35878 </code></pre>
35879  * <br><br>
35880  * The old style respose with just an array is still supported, but not recommended.
35881  * <br><br>
35882  *
35883  * A server request is sent, and child nodes are loaded only when a node is expanded.
35884  * The loading node's id is passed to the server under the parameter name "node" to
35885  * enable the server to produce the correct child nodes.
35886  * <br><br>
35887  * To pass extra parameters, an event handler may be attached to the "beforeload"
35888  * event, and the parameters specified in the TreeLoader's baseParams property:
35889  * <pre><code>
35890     myTreeLoader.on("beforeload", function(treeLoader, node) {
35891         this.baseParams.category = node.attributes.category;
35892     }, this);
35893 </code></pre><
35894  * This would pass an HTTP parameter called "category" to the server containing
35895  * the value of the Node's "category" attribute.
35896  * @constructor
35897  * Creates a new Treeloader.
35898  * @param {Object} config A config object containing config properties.
35899  */
35900 Roo.tree.TreeLoader = function(config){
35901     this.baseParams = {};
35902     this.requestMethod = "POST";
35903     Roo.apply(this, config);
35904
35905     this.addEvents({
35906     
35907         /**
35908          * @event beforeload
35909          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35910          * @param {Object} This TreeLoader object.
35911          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35912          * @param {Object} callback The callback function specified in the {@link #load} call.
35913          */
35914         beforeload : true,
35915         /**
35916          * @event load
35917          * Fires when the node has been successfuly loaded.
35918          * @param {Object} This TreeLoader object.
35919          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35920          * @param {Object} response The response object containing the data from the server.
35921          */
35922         load : true,
35923         /**
35924          * @event loadexception
35925          * Fires if the network request failed.
35926          * @param {Object} This TreeLoader object.
35927          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35928          * @param {Object} response The response object containing the data from the server.
35929          */
35930         loadexception : true,
35931         /**
35932          * @event create
35933          * Fires before a node is created, enabling you to return custom Node types 
35934          * @param {Object} This TreeLoader object.
35935          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35936          */
35937         create : true
35938     });
35939
35940     Roo.tree.TreeLoader.superclass.constructor.call(this);
35941 };
35942
35943 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35944     /**
35945     * @cfg {String} dataUrl The URL from which to request a Json string which
35946     * specifies an array of node definition object representing the child nodes
35947     * to be loaded.
35948     */
35949     /**
35950     * @cfg {String} requestMethod either GET or POST
35951     * defaults to POST (due to BC)
35952     * to be loaded.
35953     */
35954     /**
35955     * @cfg {Object} baseParams (optional) An object containing properties which
35956     * specify HTTP parameters to be passed to each request for child nodes.
35957     */
35958     /**
35959     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35960     * created by this loader. If the attributes sent by the server have an attribute in this object,
35961     * they take priority.
35962     */
35963     /**
35964     * @cfg {Object} uiProviders (optional) An object containing properties which
35965     * 
35966     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35967     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35968     * <i>uiProvider</i> attribute of a returned child node is a string rather
35969     * than a reference to a TreeNodeUI implementation, this that string value
35970     * is used as a property name in the uiProviders object. You can define the provider named
35971     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35972     */
35973     uiProviders : {},
35974
35975     /**
35976     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35977     * child nodes before loading.
35978     */
35979     clearOnLoad : true,
35980
35981     /**
35982     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35983     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35984     * Grid query { data : [ .....] }
35985     */
35986     
35987     root : false,
35988      /**
35989     * @cfg {String} queryParam (optional) 
35990     * Name of the query as it will be passed on the querystring (defaults to 'node')
35991     * eg. the request will be ?node=[id]
35992     */
35993     
35994     
35995     queryParam: false,
35996     
35997     /**
35998      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35999      * This is called automatically when a node is expanded, but may be used to reload
36000      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36001      * @param {Roo.tree.TreeNode} node
36002      * @param {Function} callback
36003      */
36004     load : function(node, callback){
36005         if(this.clearOnLoad){
36006             while(node.firstChild){
36007                 node.removeChild(node.firstChild);
36008             }
36009         }
36010         if(node.attributes.children){ // preloaded json children
36011             var cs = node.attributes.children;
36012             for(var i = 0, len = cs.length; i < len; i++){
36013                 node.appendChild(this.createNode(cs[i]));
36014             }
36015             if(typeof callback == "function"){
36016                 callback();
36017             }
36018         }else if(this.dataUrl){
36019             this.requestData(node, callback);
36020         }
36021     },
36022
36023     getParams: function(node){
36024         var buf = [], bp = this.baseParams;
36025         for(var key in bp){
36026             if(typeof bp[key] != "function"){
36027                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36028             }
36029         }
36030         var n = this.queryParam === false ? 'node' : this.queryParam;
36031         buf.push(n + "=", encodeURIComponent(node.id));
36032         return buf.join("");
36033     },
36034
36035     requestData : function(node, callback){
36036         if(this.fireEvent("beforeload", this, node, callback) !== false){
36037             this.transId = Roo.Ajax.request({
36038                 method:this.requestMethod,
36039                 url: this.dataUrl||this.url,
36040                 success: this.handleResponse,
36041                 failure: this.handleFailure,
36042                 scope: this,
36043                 argument: {callback: callback, node: node},
36044                 params: this.getParams(node)
36045             });
36046         }else{
36047             // if the load is cancelled, make sure we notify
36048             // the node that we are done
36049             if(typeof callback == "function"){
36050                 callback();
36051             }
36052         }
36053     },
36054
36055     isLoading : function(){
36056         return this.transId ? true : false;
36057     },
36058
36059     abort : function(){
36060         if(this.isLoading()){
36061             Roo.Ajax.abort(this.transId);
36062         }
36063     },
36064
36065     // private
36066     createNode : function(attr)
36067     {
36068         // apply baseAttrs, nice idea Corey!
36069         if(this.baseAttrs){
36070             Roo.applyIf(attr, this.baseAttrs);
36071         }
36072         if(this.applyLoader !== false){
36073             attr.loader = this;
36074         }
36075         // uiProvider = depreciated..
36076         
36077         if(typeof(attr.uiProvider) == 'string'){
36078            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36079                 /**  eval:var:attr */ eval(attr.uiProvider);
36080         }
36081         if(typeof(this.uiProviders['default']) != 'undefined') {
36082             attr.uiProvider = this.uiProviders['default'];
36083         }
36084         
36085         this.fireEvent('create', this, attr);
36086         
36087         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36088         return(attr.leaf ?
36089                         new Roo.tree.TreeNode(attr) :
36090                         new Roo.tree.AsyncTreeNode(attr));
36091     },
36092
36093     processResponse : function(response, node, callback)
36094     {
36095         var json = response.responseText;
36096         try {
36097             
36098             var o = Roo.decode(json);
36099             
36100             if (this.root === false && typeof(o.success) != undefined) {
36101                 this.root = 'data'; // the default behaviour for list like data..
36102                 }
36103                 
36104             if (this.root !== false &&  !o.success) {
36105                 // it's a failure condition.
36106                 var a = response.argument;
36107                 this.fireEvent("loadexception", this, a.node, response);
36108                 Roo.log("Load failed - should have a handler really");
36109                 return;
36110             }
36111             
36112             
36113             
36114             if (this.root !== false) {
36115                  o = o[this.root];
36116             }
36117             
36118             for(var i = 0, len = o.length; i < len; i++){
36119                 var n = this.createNode(o[i]);
36120                 if(n){
36121                     node.appendChild(n);
36122                 }
36123             }
36124             if(typeof callback == "function"){
36125                 callback(this, node);
36126             }
36127         }catch(e){
36128             this.handleFailure(response);
36129         }
36130     },
36131
36132     handleResponse : function(response){
36133         this.transId = false;
36134         var a = response.argument;
36135         this.processResponse(response, a.node, a.callback);
36136         this.fireEvent("load", this, a.node, response);
36137     },
36138
36139     handleFailure : function(response)
36140     {
36141         // should handle failure better..
36142         this.transId = false;
36143         var a = response.argument;
36144         this.fireEvent("loadexception", this, a.node, response);
36145         if(typeof a.callback == "function"){
36146             a.callback(this, a.node);
36147         }
36148     }
36149 });/*
36150  * Based on:
36151  * Ext JS Library 1.1.1
36152  * Copyright(c) 2006-2007, Ext JS, LLC.
36153  *
36154  * Originally Released Under LGPL - original licence link has changed is not relivant.
36155  *
36156  * Fork - LGPL
36157  * <script type="text/javascript">
36158  */
36159
36160 /**
36161 * @class Roo.tree.TreeFilter
36162 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36163 * @param {TreePanel} tree
36164 * @param {Object} config (optional)
36165  */
36166 Roo.tree.TreeFilter = function(tree, config){
36167     this.tree = tree;
36168     this.filtered = {};
36169     Roo.apply(this, config);
36170 };
36171
36172 Roo.tree.TreeFilter.prototype = {
36173     clearBlank:false,
36174     reverse:false,
36175     autoClear:false,
36176     remove:false,
36177
36178      /**
36179      * Filter the data by a specific attribute.
36180      * @param {String/RegExp} value Either string that the attribute value
36181      * should start with or a RegExp to test against the attribute
36182      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36183      * @param {TreeNode} startNode (optional) The node to start the filter at.
36184      */
36185     filter : function(value, attr, startNode){
36186         attr = attr || "text";
36187         var f;
36188         if(typeof value == "string"){
36189             var vlen = value.length;
36190             // auto clear empty filter
36191             if(vlen == 0 && this.clearBlank){
36192                 this.clear();
36193                 return;
36194             }
36195             value = value.toLowerCase();
36196             f = function(n){
36197                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36198             };
36199         }else if(value.exec){ // regex?
36200             f = function(n){
36201                 return value.test(n.attributes[attr]);
36202             };
36203         }else{
36204             throw 'Illegal filter type, must be string or regex';
36205         }
36206         this.filterBy(f, null, startNode);
36207         },
36208
36209     /**
36210      * Filter by a function. The passed function will be called with each
36211      * node in the tree (or from the startNode). If the function returns true, the node is kept
36212      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36213      * @param {Function} fn The filter function
36214      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36215      */
36216     filterBy : function(fn, scope, startNode){
36217         startNode = startNode || this.tree.root;
36218         if(this.autoClear){
36219             this.clear();
36220         }
36221         var af = this.filtered, rv = this.reverse;
36222         var f = function(n){
36223             if(n == startNode){
36224                 return true;
36225             }
36226             if(af[n.id]){
36227                 return false;
36228             }
36229             var m = fn.call(scope || n, n);
36230             if(!m || rv){
36231                 af[n.id] = n;
36232                 n.ui.hide();
36233                 return false;
36234             }
36235             return true;
36236         };
36237         startNode.cascade(f);
36238         if(this.remove){
36239            for(var id in af){
36240                if(typeof id != "function"){
36241                    var n = af[id];
36242                    if(n && n.parentNode){
36243                        n.parentNode.removeChild(n);
36244                    }
36245                }
36246            }
36247         }
36248     },
36249
36250     /**
36251      * Clears the current filter. Note: with the "remove" option
36252      * set a filter cannot be cleared.
36253      */
36254     clear : function(){
36255         var t = this.tree;
36256         var af = this.filtered;
36257         for(var id in af){
36258             if(typeof id != "function"){
36259                 var n = af[id];
36260                 if(n){
36261                     n.ui.show();
36262                 }
36263             }
36264         }
36265         this.filtered = {};
36266     }
36267 };
36268 /*
36269  * Based on:
36270  * Ext JS Library 1.1.1
36271  * Copyright(c) 2006-2007, Ext JS, LLC.
36272  *
36273  * Originally Released Under LGPL - original licence link has changed is not relivant.
36274  *
36275  * Fork - LGPL
36276  * <script type="text/javascript">
36277  */
36278  
36279
36280 /**
36281  * @class Roo.tree.TreeSorter
36282  * Provides sorting of nodes in a TreePanel
36283  * 
36284  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36285  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36286  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36287  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36288  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36289  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36290  * @constructor
36291  * @param {TreePanel} tree
36292  * @param {Object} config
36293  */
36294 Roo.tree.TreeSorter = function(tree, config){
36295     Roo.apply(this, config);
36296     tree.on("beforechildrenrendered", this.doSort, this);
36297     tree.on("append", this.updateSort, this);
36298     tree.on("insert", this.updateSort, this);
36299     
36300     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36301     var p = this.property || "text";
36302     var sortType = this.sortType;
36303     var fs = this.folderSort;
36304     var cs = this.caseSensitive === true;
36305     var leafAttr = this.leafAttr || 'leaf';
36306
36307     this.sortFn = function(n1, n2){
36308         if(fs){
36309             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36310                 return 1;
36311             }
36312             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36313                 return -1;
36314             }
36315         }
36316         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36317         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36318         if(v1 < v2){
36319                         return dsc ? +1 : -1;
36320                 }else if(v1 > v2){
36321                         return dsc ? -1 : +1;
36322         }else{
36323                 return 0;
36324         }
36325     };
36326 };
36327
36328 Roo.tree.TreeSorter.prototype = {
36329     doSort : function(node){
36330         node.sort(this.sortFn);
36331     },
36332     
36333     compareNodes : function(n1, n2){
36334         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36335     },
36336     
36337     updateSort : function(tree, node){
36338         if(node.childrenRendered){
36339             this.doSort.defer(1, this, [node]);
36340         }
36341     }
36342 };/*
36343  * Based on:
36344  * Ext JS Library 1.1.1
36345  * Copyright(c) 2006-2007, Ext JS, LLC.
36346  *
36347  * Originally Released Under LGPL - original licence link has changed is not relivant.
36348  *
36349  * Fork - LGPL
36350  * <script type="text/javascript">
36351  */
36352
36353 if(Roo.dd.DropZone){
36354     
36355 Roo.tree.TreeDropZone = function(tree, config){
36356     this.allowParentInsert = false;
36357     this.allowContainerDrop = false;
36358     this.appendOnly = false;
36359     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36360     this.tree = tree;
36361     this.lastInsertClass = "x-tree-no-status";
36362     this.dragOverData = {};
36363 };
36364
36365 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36366     ddGroup : "TreeDD",
36367     scroll:  true,
36368     
36369     expandDelay : 1000,
36370     
36371     expandNode : function(node){
36372         if(node.hasChildNodes() && !node.isExpanded()){
36373             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36374         }
36375     },
36376     
36377     queueExpand : function(node){
36378         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36379     },
36380     
36381     cancelExpand : function(){
36382         if(this.expandProcId){
36383             clearTimeout(this.expandProcId);
36384             this.expandProcId = false;
36385         }
36386     },
36387     
36388     isValidDropPoint : function(n, pt, dd, e, data){
36389         if(!n || !data){ return false; }
36390         var targetNode = n.node;
36391         var dropNode = data.node;
36392         // default drop rules
36393         if(!(targetNode && targetNode.isTarget && pt)){
36394             return false;
36395         }
36396         if(pt == "append" && targetNode.allowChildren === false){
36397             return false;
36398         }
36399         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36400             return false;
36401         }
36402         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36403             return false;
36404         }
36405         // reuse the object
36406         var overEvent = this.dragOverData;
36407         overEvent.tree = this.tree;
36408         overEvent.target = targetNode;
36409         overEvent.data = data;
36410         overEvent.point = pt;
36411         overEvent.source = dd;
36412         overEvent.rawEvent = e;
36413         overEvent.dropNode = dropNode;
36414         overEvent.cancel = false;  
36415         var result = this.tree.fireEvent("nodedragover", overEvent);
36416         return overEvent.cancel === false && result !== false;
36417     },
36418     
36419     getDropPoint : function(e, n, dd)
36420     {
36421         var tn = n.node;
36422         if(tn.isRoot){
36423             return tn.allowChildren !== false ? "append" : false; // always append for root
36424         }
36425         var dragEl = n.ddel;
36426         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36427         var y = Roo.lib.Event.getPageY(e);
36428         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36429         
36430         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36431         var noAppend = tn.allowChildren === false;
36432         if(this.appendOnly || tn.parentNode.allowChildren === false){
36433             return noAppend ? false : "append";
36434         }
36435         var noBelow = false;
36436         if(!this.allowParentInsert){
36437             noBelow = tn.hasChildNodes() && tn.isExpanded();
36438         }
36439         var q = (b - t) / (noAppend ? 2 : 3);
36440         if(y >= t && y < (t + q)){
36441             return "above";
36442         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36443             return "below";
36444         }else{
36445             return "append";
36446         }
36447     },
36448     
36449     onNodeEnter : function(n, dd, e, data)
36450     {
36451         this.cancelExpand();
36452     },
36453     
36454     onNodeOver : function(n, dd, e, data)
36455     {
36456        
36457         var pt = this.getDropPoint(e, n, dd);
36458         var node = n.node;
36459         
36460         // auto node expand check
36461         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36462             this.queueExpand(node);
36463         }else if(pt != "append"){
36464             this.cancelExpand();
36465         }
36466         
36467         // set the insert point style on the target node
36468         var returnCls = this.dropNotAllowed;
36469         if(this.isValidDropPoint(n, pt, dd, e, data)){
36470            if(pt){
36471                var el = n.ddel;
36472                var cls;
36473                if(pt == "above"){
36474                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36475                    cls = "x-tree-drag-insert-above";
36476                }else if(pt == "below"){
36477                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36478                    cls = "x-tree-drag-insert-below";
36479                }else{
36480                    returnCls = "x-tree-drop-ok-append";
36481                    cls = "x-tree-drag-append";
36482                }
36483                if(this.lastInsertClass != cls){
36484                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36485                    this.lastInsertClass = cls;
36486                }
36487            }
36488        }
36489        return returnCls;
36490     },
36491     
36492     onNodeOut : function(n, dd, e, data){
36493         
36494         this.cancelExpand();
36495         this.removeDropIndicators(n);
36496     },
36497     
36498     onNodeDrop : function(n, dd, e, data){
36499         var point = this.getDropPoint(e, n, dd);
36500         var targetNode = n.node;
36501         targetNode.ui.startDrop();
36502         if(!this.isValidDropPoint(n, point, dd, e, data)){
36503             targetNode.ui.endDrop();
36504             return false;
36505         }
36506         // first try to find the drop node
36507         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36508         var dropEvent = {
36509             tree : this.tree,
36510             target: targetNode,
36511             data: data,
36512             point: point,
36513             source: dd,
36514             rawEvent: e,
36515             dropNode: dropNode,
36516             cancel: !dropNode   
36517         };
36518         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36519         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36520             targetNode.ui.endDrop();
36521             return false;
36522         }
36523         // allow target changing
36524         targetNode = dropEvent.target;
36525         if(point == "append" && !targetNode.isExpanded()){
36526             targetNode.expand(false, null, function(){
36527                 this.completeDrop(dropEvent);
36528             }.createDelegate(this));
36529         }else{
36530             this.completeDrop(dropEvent);
36531         }
36532         return true;
36533     },
36534     
36535     completeDrop : function(de){
36536         var ns = de.dropNode, p = de.point, t = de.target;
36537         if(!(ns instanceof Array)){
36538             ns = [ns];
36539         }
36540         var n;
36541         for(var i = 0, len = ns.length; i < len; i++){
36542             n = ns[i];
36543             if(p == "above"){
36544                 t.parentNode.insertBefore(n, t);
36545             }else if(p == "below"){
36546                 t.parentNode.insertBefore(n, t.nextSibling);
36547             }else{
36548                 t.appendChild(n);
36549             }
36550         }
36551         n.ui.focus();
36552         if(this.tree.hlDrop){
36553             n.ui.highlight();
36554         }
36555         t.ui.endDrop();
36556         this.tree.fireEvent("nodedrop", de);
36557     },
36558     
36559     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36560         if(this.tree.hlDrop){
36561             dropNode.ui.focus();
36562             dropNode.ui.highlight();
36563         }
36564         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36565     },
36566     
36567     getTree : function(){
36568         return this.tree;
36569     },
36570     
36571     removeDropIndicators : function(n){
36572         if(n && n.ddel){
36573             var el = n.ddel;
36574             Roo.fly(el).removeClass([
36575                     "x-tree-drag-insert-above",
36576                     "x-tree-drag-insert-below",
36577                     "x-tree-drag-append"]);
36578             this.lastInsertClass = "_noclass";
36579         }
36580     },
36581     
36582     beforeDragDrop : function(target, e, id){
36583         this.cancelExpand();
36584         return true;
36585     },
36586     
36587     afterRepair : function(data){
36588         if(data && Roo.enableFx){
36589             data.node.ui.highlight();
36590         }
36591         this.hideProxy();
36592     } 
36593     
36594 });
36595
36596 }
36597 /*
36598  * Based on:
36599  * Ext JS Library 1.1.1
36600  * Copyright(c) 2006-2007, Ext JS, LLC.
36601  *
36602  * Originally Released Under LGPL - original licence link has changed is not relivant.
36603  *
36604  * Fork - LGPL
36605  * <script type="text/javascript">
36606  */
36607  
36608
36609 if(Roo.dd.DragZone){
36610 Roo.tree.TreeDragZone = function(tree, config){
36611     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36612     this.tree = tree;
36613 };
36614
36615 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36616     ddGroup : "TreeDD",
36617    
36618     onBeforeDrag : function(data, e){
36619         var n = data.node;
36620         return n && n.draggable && !n.disabled;
36621     },
36622      
36623     
36624     onInitDrag : function(e){
36625         var data = this.dragData;
36626         this.tree.getSelectionModel().select(data.node);
36627         this.proxy.update("");
36628         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36629         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36630     },
36631     
36632     getRepairXY : function(e, data){
36633         return data.node.ui.getDDRepairXY();
36634     },
36635     
36636     onEndDrag : function(data, e){
36637         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36638         
36639         
36640     },
36641     
36642     onValidDrop : function(dd, e, id){
36643         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36644         this.hideProxy();
36645     },
36646     
36647     beforeInvalidDrop : function(e, id){
36648         // this scrolls the original position back into view
36649         var sm = this.tree.getSelectionModel();
36650         sm.clearSelections();
36651         sm.select(this.dragData.node);
36652     }
36653 });
36654 }/*
36655  * Based on:
36656  * Ext JS Library 1.1.1
36657  * Copyright(c) 2006-2007, Ext JS, LLC.
36658  *
36659  * Originally Released Under LGPL - original licence link has changed is not relivant.
36660  *
36661  * Fork - LGPL
36662  * <script type="text/javascript">
36663  */
36664 /**
36665  * @class Roo.tree.TreeEditor
36666  * @extends Roo.Editor
36667  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36668  * as the editor field.
36669  * @constructor
36670  * @param {Object} config (used to be the tree panel.)
36671  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36672  * 
36673  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36674  * @cfg {Roo.form.TextField|Object} field The field configuration
36675  *
36676  * 
36677  */
36678 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36679     var tree = config;
36680     var field;
36681     if (oldconfig) { // old style..
36682         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36683     } else {
36684         // new style..
36685         tree = config.tree;
36686         config.field = config.field  || {};
36687         config.field.xtype = 'TextField';
36688         field = Roo.factory(config.field, Roo.form);
36689     }
36690     config = config || {};
36691     
36692     
36693     this.addEvents({
36694         /**
36695          * @event beforenodeedit
36696          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36697          * false from the handler of this event.
36698          * @param {Editor} this
36699          * @param {Roo.tree.Node} node 
36700          */
36701         "beforenodeedit" : true
36702     });
36703     
36704     //Roo.log(config);
36705     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36706
36707     this.tree = tree;
36708
36709     tree.on('beforeclick', this.beforeNodeClick, this);
36710     tree.getTreeEl().on('mousedown', this.hide, this);
36711     this.on('complete', this.updateNode, this);
36712     this.on('beforestartedit', this.fitToTree, this);
36713     this.on('startedit', this.bindScroll, this, {delay:10});
36714     this.on('specialkey', this.onSpecialKey, this);
36715 };
36716
36717 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36718     /**
36719      * @cfg {String} alignment
36720      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36721      */
36722     alignment: "l-l",
36723     // inherit
36724     autoSize: false,
36725     /**
36726      * @cfg {Boolean} hideEl
36727      * True to hide the bound element while the editor is displayed (defaults to false)
36728      */
36729     hideEl : false,
36730     /**
36731      * @cfg {String} cls
36732      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36733      */
36734     cls: "x-small-editor x-tree-editor",
36735     /**
36736      * @cfg {Boolean} shim
36737      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36738      */
36739     shim:false,
36740     // inherit
36741     shadow:"frame",
36742     /**
36743      * @cfg {Number} maxWidth
36744      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36745      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36746      * scroll and client offsets into account prior to each edit.
36747      */
36748     maxWidth: 250,
36749
36750     editDelay : 350,
36751
36752     // private
36753     fitToTree : function(ed, el){
36754         var td = this.tree.getTreeEl().dom, nd = el.dom;
36755         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36756             td.scrollLeft = nd.offsetLeft;
36757         }
36758         var w = Math.min(
36759                 this.maxWidth,
36760                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36761         this.setSize(w, '');
36762         
36763         return this.fireEvent('beforenodeedit', this, this.editNode);
36764         
36765     },
36766
36767     // private
36768     triggerEdit : function(node){
36769         this.completeEdit();
36770         this.editNode = node;
36771         this.startEdit(node.ui.textNode, node.text);
36772     },
36773
36774     // private
36775     bindScroll : function(){
36776         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36777     },
36778
36779     // private
36780     beforeNodeClick : function(node, e){
36781         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36782         this.lastClick = new Date();
36783         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36784             e.stopEvent();
36785             this.triggerEdit(node);
36786             return false;
36787         }
36788         return true;
36789     },
36790
36791     // private
36792     updateNode : function(ed, value){
36793         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36794         this.editNode.setText(value);
36795     },
36796
36797     // private
36798     onHide : function(){
36799         Roo.tree.TreeEditor.superclass.onHide.call(this);
36800         if(this.editNode){
36801             this.editNode.ui.focus();
36802         }
36803     },
36804
36805     // private
36806     onSpecialKey : function(field, e){
36807         var k = e.getKey();
36808         if(k == e.ESC){
36809             e.stopEvent();
36810             this.cancelEdit();
36811         }else if(k == e.ENTER && !e.hasModifier()){
36812             e.stopEvent();
36813             this.completeEdit();
36814         }
36815     }
36816 });//<Script type="text/javascript">
36817 /*
36818  * Based on:
36819  * Ext JS Library 1.1.1
36820  * Copyright(c) 2006-2007, Ext JS, LLC.
36821  *
36822  * Originally Released Under LGPL - original licence link has changed is not relivant.
36823  *
36824  * Fork - LGPL
36825  * <script type="text/javascript">
36826  */
36827  
36828 /**
36829  * Not documented??? - probably should be...
36830  */
36831
36832 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36833     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36834     
36835     renderElements : function(n, a, targetNode, bulkRender){
36836         //consel.log("renderElements?");
36837         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36838
36839         var t = n.getOwnerTree();
36840         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36841         
36842         var cols = t.columns;
36843         var bw = t.borderWidth;
36844         var c = cols[0];
36845         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36846          var cb = typeof a.checked == "boolean";
36847         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36848         var colcls = 'x-t-' + tid + '-c0';
36849         var buf = [
36850             '<li class="x-tree-node">',
36851             
36852                 
36853                 '<div class="x-tree-node-el ', a.cls,'">',
36854                     // extran...
36855                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36856                 
36857                 
36858                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36859                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36860                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36861                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36862                            (a.iconCls ? ' '+a.iconCls : ''),
36863                            '" unselectable="on" />',
36864                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36865                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36866                              
36867                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36868                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36869                             '<span unselectable="on" qtip="' + tx + '">',
36870                              tx,
36871                              '</span></a>' ,
36872                     '</div>',
36873                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36874                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36875                  ];
36876         for(var i = 1, len = cols.length; i < len; i++){
36877             c = cols[i];
36878             colcls = 'x-t-' + tid + '-c' +i;
36879             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36880             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36881                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36882                       "</div>");
36883          }
36884          
36885          buf.push(
36886             '</a>',
36887             '<div class="x-clear"></div></div>',
36888             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36889             "</li>");
36890         
36891         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36892             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36893                                 n.nextSibling.ui.getEl(), buf.join(""));
36894         }else{
36895             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36896         }
36897         var el = this.wrap.firstChild;
36898         this.elRow = el;
36899         this.elNode = el.firstChild;
36900         this.ranchor = el.childNodes[1];
36901         this.ctNode = this.wrap.childNodes[1];
36902         var cs = el.firstChild.childNodes;
36903         this.indentNode = cs[0];
36904         this.ecNode = cs[1];
36905         this.iconNode = cs[2];
36906         var index = 3;
36907         if(cb){
36908             this.checkbox = cs[3];
36909             index++;
36910         }
36911         this.anchor = cs[index];
36912         
36913         this.textNode = cs[index].firstChild;
36914         
36915         //el.on("click", this.onClick, this);
36916         //el.on("dblclick", this.onDblClick, this);
36917         
36918         
36919        // console.log(this);
36920     },
36921     initEvents : function(){
36922         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36923         
36924             
36925         var a = this.ranchor;
36926
36927         var el = Roo.get(a);
36928
36929         if(Roo.isOpera){ // opera render bug ignores the CSS
36930             el.setStyle("text-decoration", "none");
36931         }
36932
36933         el.on("click", this.onClick, this);
36934         el.on("dblclick", this.onDblClick, this);
36935         el.on("contextmenu", this.onContextMenu, this);
36936         
36937     },
36938     
36939     /*onSelectedChange : function(state){
36940         if(state){
36941             this.focus();
36942             this.addClass("x-tree-selected");
36943         }else{
36944             //this.blur();
36945             this.removeClass("x-tree-selected");
36946         }
36947     },*/
36948     addClass : function(cls){
36949         if(this.elRow){
36950             Roo.fly(this.elRow).addClass(cls);
36951         }
36952         
36953     },
36954     
36955     
36956     removeClass : function(cls){
36957         if(this.elRow){
36958             Roo.fly(this.elRow).removeClass(cls);
36959         }
36960     }
36961
36962     
36963     
36964 });//<Script type="text/javascript">
36965
36966 /*
36967  * Based on:
36968  * Ext JS Library 1.1.1
36969  * Copyright(c) 2006-2007, Ext JS, LLC.
36970  *
36971  * Originally Released Under LGPL - original licence link has changed is not relivant.
36972  *
36973  * Fork - LGPL
36974  * <script type="text/javascript">
36975  */
36976  
36977
36978 /**
36979  * @class Roo.tree.ColumnTree
36980  * @extends Roo.data.TreePanel
36981  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36982  * @cfg {int} borderWidth  compined right/left border allowance
36983  * @constructor
36984  * @param {String/HTMLElement/Element} el The container element
36985  * @param {Object} config
36986  */
36987 Roo.tree.ColumnTree =  function(el, config)
36988 {
36989    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36990    this.addEvents({
36991         /**
36992         * @event resize
36993         * Fire this event on a container when it resizes
36994         * @param {int} w Width
36995         * @param {int} h Height
36996         */
36997        "resize" : true
36998     });
36999     this.on('resize', this.onResize, this);
37000 };
37001
37002 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37003     //lines:false,
37004     
37005     
37006     borderWidth: Roo.isBorderBox ? 0 : 2, 
37007     headEls : false,
37008     
37009     render : function(){
37010         // add the header.....
37011        
37012         Roo.tree.ColumnTree.superclass.render.apply(this);
37013         
37014         this.el.addClass('x-column-tree');
37015         
37016         this.headers = this.el.createChild(
37017             {cls:'x-tree-headers'},this.innerCt.dom);
37018    
37019         var cols = this.columns, c;
37020         var totalWidth = 0;
37021         this.headEls = [];
37022         var  len = cols.length;
37023         for(var i = 0; i < len; i++){
37024              c = cols[i];
37025              totalWidth += c.width;
37026             this.headEls.push(this.headers.createChild({
37027                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37028                  cn: {
37029                      cls:'x-tree-hd-text',
37030                      html: c.header
37031                  },
37032                  style:'width:'+(c.width-this.borderWidth)+'px;'
37033              }));
37034         }
37035         this.headers.createChild({cls:'x-clear'});
37036         // prevent floats from wrapping when clipped
37037         this.headers.setWidth(totalWidth);
37038         //this.innerCt.setWidth(totalWidth);
37039         this.innerCt.setStyle({ overflow: 'auto' });
37040         this.onResize(this.width, this.height);
37041              
37042         
37043     },
37044     onResize : function(w,h)
37045     {
37046         this.height = h;
37047         this.width = w;
37048         // resize cols..
37049         this.innerCt.setWidth(this.width);
37050         this.innerCt.setHeight(this.height-20);
37051         
37052         // headers...
37053         var cols = this.columns, c;
37054         var totalWidth = 0;
37055         var expEl = false;
37056         var len = cols.length;
37057         for(var i = 0; i < len; i++){
37058             c = cols[i];
37059             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37060                 // it's the expander..
37061                 expEl  = this.headEls[i];
37062                 continue;
37063             }
37064             totalWidth += c.width;
37065             
37066         }
37067         if (expEl) {
37068             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37069         }
37070         this.headers.setWidth(w-20);
37071
37072         
37073         
37074         
37075     }
37076 });
37077 /*
37078  * Based on:
37079  * Ext JS Library 1.1.1
37080  * Copyright(c) 2006-2007, Ext JS, LLC.
37081  *
37082  * Originally Released Under LGPL - original licence link has changed is not relivant.
37083  *
37084  * Fork - LGPL
37085  * <script type="text/javascript">
37086  */
37087  
37088 /**
37089  * @class Roo.menu.Menu
37090  * @extends Roo.util.Observable
37091  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37092  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37093  * @constructor
37094  * Creates a new Menu
37095  * @param {Object} config Configuration options
37096  */
37097 Roo.menu.Menu = function(config){
37098     Roo.apply(this, config);
37099     this.id = this.id || Roo.id();
37100     this.addEvents({
37101         /**
37102          * @event beforeshow
37103          * Fires before this menu is displayed
37104          * @param {Roo.menu.Menu} this
37105          */
37106         beforeshow : true,
37107         /**
37108          * @event beforehide
37109          * Fires before this menu is hidden
37110          * @param {Roo.menu.Menu} this
37111          */
37112         beforehide : true,
37113         /**
37114          * @event show
37115          * Fires after this menu is displayed
37116          * @param {Roo.menu.Menu} this
37117          */
37118         show : true,
37119         /**
37120          * @event hide
37121          * Fires after this menu is hidden
37122          * @param {Roo.menu.Menu} this
37123          */
37124         hide : true,
37125         /**
37126          * @event click
37127          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37128          * @param {Roo.menu.Menu} this
37129          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37130          * @param {Roo.EventObject} e
37131          */
37132         click : true,
37133         /**
37134          * @event mouseover
37135          * Fires when the mouse is hovering over this menu
37136          * @param {Roo.menu.Menu} this
37137          * @param {Roo.EventObject} e
37138          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37139          */
37140         mouseover : true,
37141         /**
37142          * @event mouseout
37143          * Fires when the mouse exits this menu
37144          * @param {Roo.menu.Menu} this
37145          * @param {Roo.EventObject} e
37146          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37147          */
37148         mouseout : true,
37149         /**
37150          * @event itemclick
37151          * Fires when a menu item contained in this menu is clicked
37152          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37153          * @param {Roo.EventObject} e
37154          */
37155         itemclick: true
37156     });
37157     if (this.registerMenu) {
37158         Roo.menu.MenuMgr.register(this);
37159     }
37160     
37161     var mis = this.items;
37162     this.items = new Roo.util.MixedCollection();
37163     if(mis){
37164         this.add.apply(this, mis);
37165     }
37166 };
37167
37168 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37169     /**
37170      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37171      */
37172     minWidth : 120,
37173     /**
37174      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37175      * for bottom-right shadow (defaults to "sides")
37176      */
37177     shadow : "sides",
37178     /**
37179      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37180      * this menu (defaults to "tl-tr?")
37181      */
37182     subMenuAlign : "tl-tr?",
37183     /**
37184      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37185      * relative to its element of origin (defaults to "tl-bl?")
37186      */
37187     defaultAlign : "tl-bl?",
37188     /**
37189      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37190      */
37191     allowOtherMenus : false,
37192     /**
37193      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37194      */
37195     registerMenu : true,
37196
37197     hidden:true,
37198
37199     // private
37200     render : function(){
37201         if(this.el){
37202             return;
37203         }
37204         var el = this.el = new Roo.Layer({
37205             cls: "x-menu",
37206             shadow:this.shadow,
37207             constrain: false,
37208             parentEl: this.parentEl || document.body,
37209             zindex:15000
37210         });
37211
37212         this.keyNav = new Roo.menu.MenuNav(this);
37213
37214         if(this.plain){
37215             el.addClass("x-menu-plain");
37216         }
37217         if(this.cls){
37218             el.addClass(this.cls);
37219         }
37220         // generic focus element
37221         this.focusEl = el.createChild({
37222             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37223         });
37224         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37225         //disabling touch- as it's causing issues ..
37226         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37227         ul.on('click'   , this.onClick, this);
37228         
37229         
37230         ul.on("mouseover", this.onMouseOver, this);
37231         ul.on("mouseout", this.onMouseOut, this);
37232         this.items.each(function(item){
37233             if (item.hidden) {
37234                 return;
37235             }
37236             
37237             var li = document.createElement("li");
37238             li.className = "x-menu-list-item";
37239             ul.dom.appendChild(li);
37240             item.render(li, this);
37241         }, this);
37242         this.ul = ul;
37243         this.autoWidth();
37244     },
37245
37246     // private
37247     autoWidth : function(){
37248         var el = this.el, ul = this.ul;
37249         if(!el){
37250             return;
37251         }
37252         var w = this.width;
37253         if(w){
37254             el.setWidth(w);
37255         }else if(Roo.isIE){
37256             el.setWidth(this.minWidth);
37257             var t = el.dom.offsetWidth; // force recalc
37258             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37259         }
37260     },
37261
37262     // private
37263     delayAutoWidth : function(){
37264         if(this.rendered){
37265             if(!this.awTask){
37266                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37267             }
37268             this.awTask.delay(20);
37269         }
37270     },
37271
37272     // private
37273     findTargetItem : function(e){
37274         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37275         if(t && t.menuItemId){
37276             return this.items.get(t.menuItemId);
37277         }
37278     },
37279
37280     // private
37281     onClick : function(e){
37282         Roo.log("menu.onClick");
37283         var t = this.findTargetItem(e);
37284         if(!t){
37285             return;
37286         }
37287         Roo.log(e);
37288         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37289             if(t == this.activeItem && t.shouldDeactivate(e)){
37290                 this.activeItem.deactivate();
37291                 delete this.activeItem;
37292                 return;
37293             }
37294             if(t.canActivate){
37295                 this.setActiveItem(t, true);
37296             }
37297             return;
37298             
37299             
37300         }
37301         
37302         t.onClick(e);
37303         this.fireEvent("click", this, t, e);
37304     },
37305
37306     // private
37307     setActiveItem : function(item, autoExpand){
37308         if(item != this.activeItem){
37309             if(this.activeItem){
37310                 this.activeItem.deactivate();
37311             }
37312             this.activeItem = item;
37313             item.activate(autoExpand);
37314         }else if(autoExpand){
37315             item.expandMenu();
37316         }
37317     },
37318
37319     // private
37320     tryActivate : function(start, step){
37321         var items = this.items;
37322         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37323             var item = items.get(i);
37324             if(!item.disabled && item.canActivate){
37325                 this.setActiveItem(item, false);
37326                 return item;
37327             }
37328         }
37329         return false;
37330     },
37331
37332     // private
37333     onMouseOver : function(e){
37334         var t;
37335         if(t = this.findTargetItem(e)){
37336             if(t.canActivate && !t.disabled){
37337                 this.setActiveItem(t, true);
37338             }
37339         }
37340         this.fireEvent("mouseover", this, e, t);
37341     },
37342
37343     // private
37344     onMouseOut : function(e){
37345         var t;
37346         if(t = this.findTargetItem(e)){
37347             if(t == this.activeItem && t.shouldDeactivate(e)){
37348                 this.activeItem.deactivate();
37349                 delete this.activeItem;
37350             }
37351         }
37352         this.fireEvent("mouseout", this, e, t);
37353     },
37354
37355     /**
37356      * Read-only.  Returns true if the menu is currently displayed, else false.
37357      * @type Boolean
37358      */
37359     isVisible : function(){
37360         return this.el && !this.hidden;
37361     },
37362
37363     /**
37364      * Displays this menu relative to another element
37365      * @param {String/HTMLElement/Roo.Element} element The element to align to
37366      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37367      * the element (defaults to this.defaultAlign)
37368      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37369      */
37370     show : function(el, pos, parentMenu){
37371         this.parentMenu = parentMenu;
37372         if(!this.el){
37373             this.render();
37374         }
37375         this.fireEvent("beforeshow", this);
37376         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37377     },
37378
37379     /**
37380      * Displays this menu at a specific xy position
37381      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37382      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37383      */
37384     showAt : function(xy, parentMenu, /* private: */_e){
37385         this.parentMenu = parentMenu;
37386         if(!this.el){
37387             this.render();
37388         }
37389         if(_e !== false){
37390             this.fireEvent("beforeshow", this);
37391             xy = this.el.adjustForConstraints(xy);
37392         }
37393         this.el.setXY(xy);
37394         this.el.show();
37395         this.hidden = false;
37396         this.focus();
37397         this.fireEvent("show", this);
37398     },
37399
37400     focus : function(){
37401         if(!this.hidden){
37402             this.doFocus.defer(50, this);
37403         }
37404     },
37405
37406     doFocus : function(){
37407         if(!this.hidden){
37408             this.focusEl.focus();
37409         }
37410     },
37411
37412     /**
37413      * Hides this menu and optionally all parent menus
37414      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37415      */
37416     hide : function(deep){
37417         if(this.el && this.isVisible()){
37418             this.fireEvent("beforehide", this);
37419             if(this.activeItem){
37420                 this.activeItem.deactivate();
37421                 this.activeItem = null;
37422             }
37423             this.el.hide();
37424             this.hidden = true;
37425             this.fireEvent("hide", this);
37426         }
37427         if(deep === true && this.parentMenu){
37428             this.parentMenu.hide(true);
37429         }
37430     },
37431
37432     /**
37433      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37434      * Any of the following are valid:
37435      * <ul>
37436      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37437      * <li>An HTMLElement object which will be converted to a menu item</li>
37438      * <li>A menu item config object that will be created as a new menu item</li>
37439      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37440      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37441      * </ul>
37442      * Usage:
37443      * <pre><code>
37444 // Create the menu
37445 var menu = new Roo.menu.Menu();
37446
37447 // Create a menu item to add by reference
37448 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37449
37450 // Add a bunch of items at once using different methods.
37451 // Only the last item added will be returned.
37452 var item = menu.add(
37453     menuItem,                // add existing item by ref
37454     'Dynamic Item',          // new TextItem
37455     '-',                     // new separator
37456     { text: 'Config Item' }  // new item by config
37457 );
37458 </code></pre>
37459      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37460      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37461      */
37462     add : function(){
37463         var a = arguments, l = a.length, item;
37464         for(var i = 0; i < l; i++){
37465             var el = a[i];
37466             if ((typeof(el) == "object") && el.xtype && el.xns) {
37467                 el = Roo.factory(el, Roo.menu);
37468             }
37469             
37470             if(el.render){ // some kind of Item
37471                 item = this.addItem(el);
37472             }else if(typeof el == "string"){ // string
37473                 if(el == "separator" || el == "-"){
37474                     item = this.addSeparator();
37475                 }else{
37476                     item = this.addText(el);
37477                 }
37478             }else if(el.tagName || el.el){ // element
37479                 item = this.addElement(el);
37480             }else if(typeof el == "object"){ // must be menu item config?
37481                 item = this.addMenuItem(el);
37482             }
37483         }
37484         return item;
37485     },
37486
37487     /**
37488      * Returns this menu's underlying {@link Roo.Element} object
37489      * @return {Roo.Element} The element
37490      */
37491     getEl : function(){
37492         if(!this.el){
37493             this.render();
37494         }
37495         return this.el;
37496     },
37497
37498     /**
37499      * Adds a separator bar to the menu
37500      * @return {Roo.menu.Item} The menu item that was added
37501      */
37502     addSeparator : function(){
37503         return this.addItem(new Roo.menu.Separator());
37504     },
37505
37506     /**
37507      * Adds an {@link Roo.Element} object to the menu
37508      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37509      * @return {Roo.menu.Item} The menu item that was added
37510      */
37511     addElement : function(el){
37512         return this.addItem(new Roo.menu.BaseItem(el));
37513     },
37514
37515     /**
37516      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37517      * @param {Roo.menu.Item} item The menu item to add
37518      * @return {Roo.menu.Item} The menu item that was added
37519      */
37520     addItem : function(item){
37521         this.items.add(item);
37522         if(this.ul){
37523             var li = document.createElement("li");
37524             li.className = "x-menu-list-item";
37525             this.ul.dom.appendChild(li);
37526             item.render(li, this);
37527             this.delayAutoWidth();
37528         }
37529         return item;
37530     },
37531
37532     /**
37533      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37534      * @param {Object} config A MenuItem config object
37535      * @return {Roo.menu.Item} The menu item that was added
37536      */
37537     addMenuItem : function(config){
37538         if(!(config instanceof Roo.menu.Item)){
37539             if(typeof config.checked == "boolean"){ // must be check menu item config?
37540                 config = new Roo.menu.CheckItem(config);
37541             }else{
37542                 config = new Roo.menu.Item(config);
37543             }
37544         }
37545         return this.addItem(config);
37546     },
37547
37548     /**
37549      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37550      * @param {String} text The text to display in the menu item
37551      * @return {Roo.menu.Item} The menu item that was added
37552      */
37553     addText : function(text){
37554         return this.addItem(new Roo.menu.TextItem({ text : text }));
37555     },
37556
37557     /**
37558      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37559      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37560      * @param {Roo.menu.Item} item The menu item to add
37561      * @return {Roo.menu.Item} The menu item that was added
37562      */
37563     insert : function(index, item){
37564         this.items.insert(index, item);
37565         if(this.ul){
37566             var li = document.createElement("li");
37567             li.className = "x-menu-list-item";
37568             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37569             item.render(li, this);
37570             this.delayAutoWidth();
37571         }
37572         return item;
37573     },
37574
37575     /**
37576      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37577      * @param {Roo.menu.Item} item The menu item to remove
37578      */
37579     remove : function(item){
37580         this.items.removeKey(item.id);
37581         item.destroy();
37582     },
37583
37584     /**
37585      * Removes and destroys all items in the menu
37586      */
37587     removeAll : function(){
37588         var f;
37589         while(f = this.items.first()){
37590             this.remove(f);
37591         }
37592     }
37593 });
37594
37595 // MenuNav is a private utility class used internally by the Menu
37596 Roo.menu.MenuNav = function(menu){
37597     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37598     this.scope = this.menu = menu;
37599 };
37600
37601 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37602     doRelay : function(e, h){
37603         var k = e.getKey();
37604         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37605             this.menu.tryActivate(0, 1);
37606             return false;
37607         }
37608         return h.call(this.scope || this, e, this.menu);
37609     },
37610
37611     up : function(e, m){
37612         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37613             m.tryActivate(m.items.length-1, -1);
37614         }
37615     },
37616
37617     down : function(e, m){
37618         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37619             m.tryActivate(0, 1);
37620         }
37621     },
37622
37623     right : function(e, m){
37624         if(m.activeItem){
37625             m.activeItem.expandMenu(true);
37626         }
37627     },
37628
37629     left : function(e, m){
37630         m.hide();
37631         if(m.parentMenu && m.parentMenu.activeItem){
37632             m.parentMenu.activeItem.activate();
37633         }
37634     },
37635
37636     enter : function(e, m){
37637         if(m.activeItem){
37638             e.stopPropagation();
37639             m.activeItem.onClick(e);
37640             m.fireEvent("click", this, m.activeItem);
37641             return true;
37642         }
37643     }
37644 });/*
37645  * Based on:
37646  * Ext JS Library 1.1.1
37647  * Copyright(c) 2006-2007, Ext JS, LLC.
37648  *
37649  * Originally Released Under LGPL - original licence link has changed is not relivant.
37650  *
37651  * Fork - LGPL
37652  * <script type="text/javascript">
37653  */
37654  
37655 /**
37656  * @class Roo.menu.MenuMgr
37657  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37658  * @singleton
37659  */
37660 Roo.menu.MenuMgr = function(){
37661    var menus, active, groups = {}, attached = false, lastShow = new Date();
37662
37663    // private - called when first menu is created
37664    function init(){
37665        menus = {};
37666        active = new Roo.util.MixedCollection();
37667        Roo.get(document).addKeyListener(27, function(){
37668            if(active.length > 0){
37669                hideAll();
37670            }
37671        });
37672    }
37673
37674    // private
37675    function hideAll(){
37676        if(active && active.length > 0){
37677            var c = active.clone();
37678            c.each(function(m){
37679                m.hide();
37680            });
37681        }
37682    }
37683
37684    // private
37685    function onHide(m){
37686        active.remove(m);
37687        if(active.length < 1){
37688            Roo.get(document).un("mousedown", onMouseDown);
37689            attached = false;
37690        }
37691    }
37692
37693    // private
37694    function onShow(m){
37695        var last = active.last();
37696        lastShow = new Date();
37697        active.add(m);
37698        if(!attached){
37699            Roo.get(document).on("mousedown", onMouseDown);
37700            attached = true;
37701        }
37702        if(m.parentMenu){
37703           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37704           m.parentMenu.activeChild = m;
37705        }else if(last && last.isVisible()){
37706           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37707        }
37708    }
37709
37710    // private
37711    function onBeforeHide(m){
37712        if(m.activeChild){
37713            m.activeChild.hide();
37714        }
37715        if(m.autoHideTimer){
37716            clearTimeout(m.autoHideTimer);
37717            delete m.autoHideTimer;
37718        }
37719    }
37720
37721    // private
37722    function onBeforeShow(m){
37723        var pm = m.parentMenu;
37724        if(!pm && !m.allowOtherMenus){
37725            hideAll();
37726        }else if(pm && pm.activeChild && active != m){
37727            pm.activeChild.hide();
37728        }
37729    }
37730
37731    // private
37732    function onMouseDown(e){
37733        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37734            hideAll();
37735        }
37736    }
37737
37738    // private
37739    function onBeforeCheck(mi, state){
37740        if(state){
37741            var g = groups[mi.group];
37742            for(var i = 0, l = g.length; i < l; i++){
37743                if(g[i] != mi){
37744                    g[i].setChecked(false);
37745                }
37746            }
37747        }
37748    }
37749
37750    return {
37751
37752        /**
37753         * Hides all menus that are currently visible
37754         */
37755        hideAll : function(){
37756             hideAll();  
37757        },
37758
37759        // private
37760        register : function(menu){
37761            if(!menus){
37762                init();
37763            }
37764            menus[menu.id] = menu;
37765            menu.on("beforehide", onBeforeHide);
37766            menu.on("hide", onHide);
37767            menu.on("beforeshow", onBeforeShow);
37768            menu.on("show", onShow);
37769            var g = menu.group;
37770            if(g && menu.events["checkchange"]){
37771                if(!groups[g]){
37772                    groups[g] = [];
37773                }
37774                groups[g].push(menu);
37775                menu.on("checkchange", onCheck);
37776            }
37777        },
37778
37779         /**
37780          * Returns a {@link Roo.menu.Menu} object
37781          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37782          * be used to generate and return a new Menu instance.
37783          */
37784        get : function(menu){
37785            if(typeof menu == "string"){ // menu id
37786                return menus[menu];
37787            }else if(menu.events){  // menu instance
37788                return menu;
37789            }else if(typeof menu.length == 'number'){ // array of menu items?
37790                return new Roo.menu.Menu({items:menu});
37791            }else{ // otherwise, must be a config
37792                return new Roo.menu.Menu(menu);
37793            }
37794        },
37795
37796        // private
37797        unregister : function(menu){
37798            delete menus[menu.id];
37799            menu.un("beforehide", onBeforeHide);
37800            menu.un("hide", onHide);
37801            menu.un("beforeshow", onBeforeShow);
37802            menu.un("show", onShow);
37803            var g = menu.group;
37804            if(g && menu.events["checkchange"]){
37805                groups[g].remove(menu);
37806                menu.un("checkchange", onCheck);
37807            }
37808        },
37809
37810        // private
37811        registerCheckable : function(menuItem){
37812            var g = menuItem.group;
37813            if(g){
37814                if(!groups[g]){
37815                    groups[g] = [];
37816                }
37817                groups[g].push(menuItem);
37818                menuItem.on("beforecheckchange", onBeforeCheck);
37819            }
37820        },
37821
37822        // private
37823        unregisterCheckable : function(menuItem){
37824            var g = menuItem.group;
37825            if(g){
37826                groups[g].remove(menuItem);
37827                menuItem.un("beforecheckchange", onBeforeCheck);
37828            }
37829        }
37830    };
37831 }();/*
37832  * Based on:
37833  * Ext JS Library 1.1.1
37834  * Copyright(c) 2006-2007, Ext JS, LLC.
37835  *
37836  * Originally Released Under LGPL - original licence link has changed is not relivant.
37837  *
37838  * Fork - LGPL
37839  * <script type="text/javascript">
37840  */
37841  
37842
37843 /**
37844  * @class Roo.menu.BaseItem
37845  * @extends Roo.Component
37846  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37847  * management and base configuration options shared by all menu components.
37848  * @constructor
37849  * Creates a new BaseItem
37850  * @param {Object} config Configuration options
37851  */
37852 Roo.menu.BaseItem = function(config){
37853     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37854
37855     this.addEvents({
37856         /**
37857          * @event click
37858          * Fires when this item is clicked
37859          * @param {Roo.menu.BaseItem} this
37860          * @param {Roo.EventObject} e
37861          */
37862         click: true,
37863         /**
37864          * @event activate
37865          * Fires when this item is activated
37866          * @param {Roo.menu.BaseItem} this
37867          */
37868         activate : true,
37869         /**
37870          * @event deactivate
37871          * Fires when this item is deactivated
37872          * @param {Roo.menu.BaseItem} this
37873          */
37874         deactivate : true
37875     });
37876
37877     if(this.handler){
37878         this.on("click", this.handler, this.scope, true);
37879     }
37880 };
37881
37882 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37883     /**
37884      * @cfg {Function} handler
37885      * A function that will handle the click event of this menu item (defaults to undefined)
37886      */
37887     /**
37888      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37889      */
37890     canActivate : false,
37891     
37892      /**
37893      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37894      */
37895     hidden: false,
37896     
37897     /**
37898      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37899      */
37900     activeClass : "x-menu-item-active",
37901     /**
37902      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37903      */
37904     hideOnClick : true,
37905     /**
37906      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37907      */
37908     hideDelay : 100,
37909
37910     // private
37911     ctype: "Roo.menu.BaseItem",
37912
37913     // private
37914     actionMode : "container",
37915
37916     // private
37917     render : function(container, parentMenu){
37918         this.parentMenu = parentMenu;
37919         Roo.menu.BaseItem.superclass.render.call(this, container);
37920         this.container.menuItemId = this.id;
37921     },
37922
37923     // private
37924     onRender : function(container, position){
37925         this.el = Roo.get(this.el);
37926         container.dom.appendChild(this.el.dom);
37927     },
37928
37929     // private
37930     onClick : function(e){
37931         if(!this.disabled && this.fireEvent("click", this, e) !== false
37932                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37933             this.handleClick(e);
37934         }else{
37935             e.stopEvent();
37936         }
37937     },
37938
37939     // private
37940     activate : function(){
37941         if(this.disabled){
37942             return false;
37943         }
37944         var li = this.container;
37945         li.addClass(this.activeClass);
37946         this.region = li.getRegion().adjust(2, 2, -2, -2);
37947         this.fireEvent("activate", this);
37948         return true;
37949     },
37950
37951     // private
37952     deactivate : function(){
37953         this.container.removeClass(this.activeClass);
37954         this.fireEvent("deactivate", this);
37955     },
37956
37957     // private
37958     shouldDeactivate : function(e){
37959         return !this.region || !this.region.contains(e.getPoint());
37960     },
37961
37962     // private
37963     handleClick : function(e){
37964         if(this.hideOnClick){
37965             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37966         }
37967     },
37968
37969     // private
37970     expandMenu : function(autoActivate){
37971         // do nothing
37972     },
37973
37974     // private
37975     hideMenu : function(){
37976         // do nothing
37977     }
37978 });/*
37979  * Based on:
37980  * Ext JS Library 1.1.1
37981  * Copyright(c) 2006-2007, Ext JS, LLC.
37982  *
37983  * Originally Released Under LGPL - original licence link has changed is not relivant.
37984  *
37985  * Fork - LGPL
37986  * <script type="text/javascript">
37987  */
37988  
37989 /**
37990  * @class Roo.menu.Adapter
37991  * @extends Roo.menu.BaseItem
37992  * 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.
37993  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37994  * @constructor
37995  * Creates a new Adapter
37996  * @param {Object} config Configuration options
37997  */
37998 Roo.menu.Adapter = function(component, config){
37999     Roo.menu.Adapter.superclass.constructor.call(this, config);
38000     this.component = component;
38001 };
38002 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38003     // private
38004     canActivate : true,
38005
38006     // private
38007     onRender : function(container, position){
38008         this.component.render(container);
38009         this.el = this.component.getEl();
38010     },
38011
38012     // private
38013     activate : function(){
38014         if(this.disabled){
38015             return false;
38016         }
38017         this.component.focus();
38018         this.fireEvent("activate", this);
38019         return true;
38020     },
38021
38022     // private
38023     deactivate : function(){
38024         this.fireEvent("deactivate", this);
38025     },
38026
38027     // private
38028     disable : function(){
38029         this.component.disable();
38030         Roo.menu.Adapter.superclass.disable.call(this);
38031     },
38032
38033     // private
38034     enable : function(){
38035         this.component.enable();
38036         Roo.menu.Adapter.superclass.enable.call(this);
38037     }
38038 });/*
38039  * Based on:
38040  * Ext JS Library 1.1.1
38041  * Copyright(c) 2006-2007, Ext JS, LLC.
38042  *
38043  * Originally Released Under LGPL - original licence link has changed is not relivant.
38044  *
38045  * Fork - LGPL
38046  * <script type="text/javascript">
38047  */
38048
38049 /**
38050  * @class Roo.menu.TextItem
38051  * @extends Roo.menu.BaseItem
38052  * Adds a static text string to a menu, usually used as either a heading or group separator.
38053  * Note: old style constructor with text is still supported.
38054  * 
38055  * @constructor
38056  * Creates a new TextItem
38057  * @param {Object} cfg Configuration
38058  */
38059 Roo.menu.TextItem = function(cfg){
38060     if (typeof(cfg) == 'string') {
38061         this.text = cfg;
38062     } else {
38063         Roo.apply(this,cfg);
38064     }
38065     
38066     Roo.menu.TextItem.superclass.constructor.call(this);
38067 };
38068
38069 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38070     /**
38071      * @cfg {Boolean} text Text to show on item.
38072      */
38073     text : '',
38074     
38075     /**
38076      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38077      */
38078     hideOnClick : false,
38079     /**
38080      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38081      */
38082     itemCls : "x-menu-text",
38083
38084     // private
38085     onRender : function(){
38086         var s = document.createElement("span");
38087         s.className = this.itemCls;
38088         s.innerHTML = this.text;
38089         this.el = s;
38090         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38091     }
38092 });/*
38093  * Based on:
38094  * Ext JS Library 1.1.1
38095  * Copyright(c) 2006-2007, Ext JS, LLC.
38096  *
38097  * Originally Released Under LGPL - original licence link has changed is not relivant.
38098  *
38099  * Fork - LGPL
38100  * <script type="text/javascript">
38101  */
38102
38103 /**
38104  * @class Roo.menu.Separator
38105  * @extends Roo.menu.BaseItem
38106  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38107  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38108  * @constructor
38109  * @param {Object} config Configuration options
38110  */
38111 Roo.menu.Separator = function(config){
38112     Roo.menu.Separator.superclass.constructor.call(this, config);
38113 };
38114
38115 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38116     /**
38117      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38118      */
38119     itemCls : "x-menu-sep",
38120     /**
38121      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38122      */
38123     hideOnClick : false,
38124
38125     // private
38126     onRender : function(li){
38127         var s = document.createElement("span");
38128         s.className = this.itemCls;
38129         s.innerHTML = "&#160;";
38130         this.el = s;
38131         li.addClass("x-menu-sep-li");
38132         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38133     }
38134 });/*
38135  * Based on:
38136  * Ext JS Library 1.1.1
38137  * Copyright(c) 2006-2007, Ext JS, LLC.
38138  *
38139  * Originally Released Under LGPL - original licence link has changed is not relivant.
38140  *
38141  * Fork - LGPL
38142  * <script type="text/javascript">
38143  */
38144 /**
38145  * @class Roo.menu.Item
38146  * @extends Roo.menu.BaseItem
38147  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38148  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38149  * activation and click handling.
38150  * @constructor
38151  * Creates a new Item
38152  * @param {Object} config Configuration options
38153  */
38154 Roo.menu.Item = function(config){
38155     Roo.menu.Item.superclass.constructor.call(this, config);
38156     if(this.menu){
38157         this.menu = Roo.menu.MenuMgr.get(this.menu);
38158     }
38159 };
38160 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38161     
38162     /**
38163      * @cfg {String} text
38164      * The text to show on the menu item.
38165      */
38166     text: '',
38167      /**
38168      * @cfg {String} HTML to render in menu
38169      * The text to show on the menu item (HTML version).
38170      */
38171     html: '',
38172     /**
38173      * @cfg {String} icon
38174      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38175      */
38176     icon: undefined,
38177     /**
38178      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38179      */
38180     itemCls : "x-menu-item",
38181     /**
38182      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38183      */
38184     canActivate : true,
38185     /**
38186      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38187      */
38188     showDelay: 200,
38189     // doc'd in BaseItem
38190     hideDelay: 200,
38191
38192     // private
38193     ctype: "Roo.menu.Item",
38194     
38195     // private
38196     onRender : function(container, position){
38197         var el = document.createElement("a");
38198         el.hideFocus = true;
38199         el.unselectable = "on";
38200         el.href = this.href || "#";
38201         if(this.hrefTarget){
38202             el.target = this.hrefTarget;
38203         }
38204         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38205         
38206         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38207         
38208         el.innerHTML = String.format(
38209                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38210                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38211         this.el = el;
38212         Roo.menu.Item.superclass.onRender.call(this, container, position);
38213     },
38214
38215     /**
38216      * Sets the text to display in this menu item
38217      * @param {String} text The text to display
38218      * @param {Boolean} isHTML true to indicate text is pure html.
38219      */
38220     setText : function(text, isHTML){
38221         if (isHTML) {
38222             this.html = text;
38223         } else {
38224             this.text = text;
38225             this.html = '';
38226         }
38227         if(this.rendered){
38228             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38229      
38230             this.el.update(String.format(
38231                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38232                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38233             this.parentMenu.autoWidth();
38234         }
38235     },
38236
38237     // private
38238     handleClick : function(e){
38239         if(!this.href){ // if no link defined, stop the event automatically
38240             e.stopEvent();
38241         }
38242         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38243     },
38244
38245     // private
38246     activate : function(autoExpand){
38247         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38248             this.focus();
38249             if(autoExpand){
38250                 this.expandMenu();
38251             }
38252         }
38253         return true;
38254     },
38255
38256     // private
38257     shouldDeactivate : function(e){
38258         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38259             if(this.menu && this.menu.isVisible()){
38260                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38261             }
38262             return true;
38263         }
38264         return false;
38265     },
38266
38267     // private
38268     deactivate : function(){
38269         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38270         this.hideMenu();
38271     },
38272
38273     // private
38274     expandMenu : function(autoActivate){
38275         if(!this.disabled && this.menu){
38276             clearTimeout(this.hideTimer);
38277             delete this.hideTimer;
38278             if(!this.menu.isVisible() && !this.showTimer){
38279                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38280             }else if (this.menu.isVisible() && autoActivate){
38281                 this.menu.tryActivate(0, 1);
38282             }
38283         }
38284     },
38285
38286     // private
38287     deferExpand : function(autoActivate){
38288         delete this.showTimer;
38289         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38290         if(autoActivate){
38291             this.menu.tryActivate(0, 1);
38292         }
38293     },
38294
38295     // private
38296     hideMenu : function(){
38297         clearTimeout(this.showTimer);
38298         delete this.showTimer;
38299         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38300             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38301         }
38302     },
38303
38304     // private
38305     deferHide : function(){
38306         delete this.hideTimer;
38307         this.menu.hide();
38308     }
38309 });/*
38310  * Based on:
38311  * Ext JS Library 1.1.1
38312  * Copyright(c) 2006-2007, Ext JS, LLC.
38313  *
38314  * Originally Released Under LGPL - original licence link has changed is not relivant.
38315  *
38316  * Fork - LGPL
38317  * <script type="text/javascript">
38318  */
38319  
38320 /**
38321  * @class Roo.menu.CheckItem
38322  * @extends Roo.menu.Item
38323  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38324  * @constructor
38325  * Creates a new CheckItem
38326  * @param {Object} config Configuration options
38327  */
38328 Roo.menu.CheckItem = function(config){
38329     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38330     this.addEvents({
38331         /**
38332          * @event beforecheckchange
38333          * Fires before the checked value is set, providing an opportunity to cancel if needed
38334          * @param {Roo.menu.CheckItem} this
38335          * @param {Boolean} checked The new checked value that will be set
38336          */
38337         "beforecheckchange" : true,
38338         /**
38339          * @event checkchange
38340          * Fires after the checked value has been set
38341          * @param {Roo.menu.CheckItem} this
38342          * @param {Boolean} checked The checked value that was set
38343          */
38344         "checkchange" : true
38345     });
38346     if(this.checkHandler){
38347         this.on('checkchange', this.checkHandler, this.scope);
38348     }
38349 };
38350 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38351     /**
38352      * @cfg {String} group
38353      * All check items with the same group name will automatically be grouped into a single-select
38354      * radio button group (defaults to '')
38355      */
38356     /**
38357      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38358      */
38359     itemCls : "x-menu-item x-menu-check-item",
38360     /**
38361      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38362      */
38363     groupClass : "x-menu-group-item",
38364
38365     /**
38366      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38367      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38368      * initialized with checked = true will be rendered as checked.
38369      */
38370     checked: false,
38371
38372     // private
38373     ctype: "Roo.menu.CheckItem",
38374
38375     // private
38376     onRender : function(c){
38377         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38378         if(this.group){
38379             this.el.addClass(this.groupClass);
38380         }
38381         Roo.menu.MenuMgr.registerCheckable(this);
38382         if(this.checked){
38383             this.checked = false;
38384             this.setChecked(true, true);
38385         }
38386     },
38387
38388     // private
38389     destroy : function(){
38390         if(this.rendered){
38391             Roo.menu.MenuMgr.unregisterCheckable(this);
38392         }
38393         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38394     },
38395
38396     /**
38397      * Set the checked state of this item
38398      * @param {Boolean} checked The new checked value
38399      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38400      */
38401     setChecked : function(state, suppressEvent){
38402         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38403             if(this.container){
38404                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38405             }
38406             this.checked = state;
38407             if(suppressEvent !== true){
38408                 this.fireEvent("checkchange", this, state);
38409             }
38410         }
38411     },
38412
38413     // private
38414     handleClick : function(e){
38415        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38416            this.setChecked(!this.checked);
38417        }
38418        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38419     }
38420 });/*
38421  * Based on:
38422  * Ext JS Library 1.1.1
38423  * Copyright(c) 2006-2007, Ext JS, LLC.
38424  *
38425  * Originally Released Under LGPL - original licence link has changed is not relivant.
38426  *
38427  * Fork - LGPL
38428  * <script type="text/javascript">
38429  */
38430  
38431 /**
38432  * @class Roo.menu.DateItem
38433  * @extends Roo.menu.Adapter
38434  * A menu item that wraps the {@link Roo.DatPicker} component.
38435  * @constructor
38436  * Creates a new DateItem
38437  * @param {Object} config Configuration options
38438  */
38439 Roo.menu.DateItem = function(config){
38440     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38441     /** The Roo.DatePicker object @type Roo.DatePicker */
38442     this.picker = this.component;
38443     this.addEvents({select: true});
38444     
38445     this.picker.on("render", function(picker){
38446         picker.getEl().swallowEvent("click");
38447         picker.container.addClass("x-menu-date-item");
38448     });
38449
38450     this.picker.on("select", this.onSelect, this);
38451 };
38452
38453 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38454     // private
38455     onSelect : function(picker, date){
38456         this.fireEvent("select", this, date, picker);
38457         Roo.menu.DateItem.superclass.handleClick.call(this);
38458     }
38459 });/*
38460  * Based on:
38461  * Ext JS Library 1.1.1
38462  * Copyright(c) 2006-2007, Ext JS, LLC.
38463  *
38464  * Originally Released Under LGPL - original licence link has changed is not relivant.
38465  *
38466  * Fork - LGPL
38467  * <script type="text/javascript">
38468  */
38469  
38470 /**
38471  * @class Roo.menu.ColorItem
38472  * @extends Roo.menu.Adapter
38473  * A menu item that wraps the {@link Roo.ColorPalette} component.
38474  * @constructor
38475  * Creates a new ColorItem
38476  * @param {Object} config Configuration options
38477  */
38478 Roo.menu.ColorItem = function(config){
38479     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38480     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38481     this.palette = this.component;
38482     this.relayEvents(this.palette, ["select"]);
38483     if(this.selectHandler){
38484         this.on('select', this.selectHandler, this.scope);
38485     }
38486 };
38487 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38488  * Based on:
38489  * Ext JS Library 1.1.1
38490  * Copyright(c) 2006-2007, Ext JS, LLC.
38491  *
38492  * Originally Released Under LGPL - original licence link has changed is not relivant.
38493  *
38494  * Fork - LGPL
38495  * <script type="text/javascript">
38496  */
38497  
38498
38499 /**
38500  * @class Roo.menu.DateMenu
38501  * @extends Roo.menu.Menu
38502  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38503  * @constructor
38504  * Creates a new DateMenu
38505  * @param {Object} config Configuration options
38506  */
38507 Roo.menu.DateMenu = function(config){
38508     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38509     this.plain = true;
38510     var di = new Roo.menu.DateItem(config);
38511     this.add(di);
38512     /**
38513      * The {@link Roo.DatePicker} instance for this DateMenu
38514      * @type DatePicker
38515      */
38516     this.picker = di.picker;
38517     /**
38518      * @event select
38519      * @param {DatePicker} picker
38520      * @param {Date} date
38521      */
38522     this.relayEvents(di, ["select"]);
38523     this.on('beforeshow', function(){
38524         if(this.picker){
38525             this.picker.hideMonthPicker(false);
38526         }
38527     }, this);
38528 };
38529 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38530     cls:'x-date-menu'
38531 });/*
38532  * Based on:
38533  * Ext JS Library 1.1.1
38534  * Copyright(c) 2006-2007, Ext JS, LLC.
38535  *
38536  * Originally Released Under LGPL - original licence link has changed is not relivant.
38537  *
38538  * Fork - LGPL
38539  * <script type="text/javascript">
38540  */
38541  
38542
38543 /**
38544  * @class Roo.menu.ColorMenu
38545  * @extends Roo.menu.Menu
38546  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38547  * @constructor
38548  * Creates a new ColorMenu
38549  * @param {Object} config Configuration options
38550  */
38551 Roo.menu.ColorMenu = function(config){
38552     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38553     this.plain = true;
38554     var ci = new Roo.menu.ColorItem(config);
38555     this.add(ci);
38556     /**
38557      * The {@link Roo.ColorPalette} instance for this ColorMenu
38558      * @type ColorPalette
38559      */
38560     this.palette = ci.palette;
38561     /**
38562      * @event select
38563      * @param {ColorPalette} palette
38564      * @param {String} color
38565      */
38566     this.relayEvents(ci, ["select"]);
38567 };
38568 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38569  * Based on:
38570  * Ext JS Library 1.1.1
38571  * Copyright(c) 2006-2007, Ext JS, LLC.
38572  *
38573  * Originally Released Under LGPL - original licence link has changed is not relivant.
38574  *
38575  * Fork - LGPL
38576  * <script type="text/javascript">
38577  */
38578  
38579 /**
38580  * @class Roo.form.Field
38581  * @extends Roo.BoxComponent
38582  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38583  * @constructor
38584  * Creates a new Field
38585  * @param {Object} config Configuration options
38586  */
38587 Roo.form.Field = function(config){
38588     Roo.form.Field.superclass.constructor.call(this, config);
38589 };
38590
38591 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38592     /**
38593      * @cfg {String} fieldLabel Label to use when rendering a form.
38594      */
38595        /**
38596      * @cfg {String} qtip Mouse over tip
38597      */
38598      
38599     /**
38600      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38601      */
38602     invalidClass : "x-form-invalid",
38603     /**
38604      * @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")
38605      */
38606     invalidText : "The value in this field is invalid",
38607     /**
38608      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38609      */
38610     focusClass : "x-form-focus",
38611     /**
38612      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38613       automatic validation (defaults to "keyup").
38614      */
38615     validationEvent : "keyup",
38616     /**
38617      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38618      */
38619     validateOnBlur : true,
38620     /**
38621      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38622      */
38623     validationDelay : 250,
38624     /**
38625      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38626      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38627      */
38628     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38629     /**
38630      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38631      */
38632     fieldClass : "x-form-field",
38633     /**
38634      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38635      *<pre>
38636 Value         Description
38637 -----------   ----------------------------------------------------------------------
38638 qtip          Display a quick tip when the user hovers over the field
38639 title         Display a default browser title attribute popup
38640 under         Add a block div beneath the field containing the error text
38641 side          Add an error icon to the right of the field with a popup on hover
38642 [element id]  Add the error text directly to the innerHTML of the specified element
38643 </pre>
38644      */
38645     msgTarget : 'qtip',
38646     /**
38647      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38648      */
38649     msgFx : 'normal',
38650
38651     /**
38652      * @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.
38653      */
38654     readOnly : false,
38655
38656     /**
38657      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38658      */
38659     disabled : false,
38660
38661     /**
38662      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38663      */
38664     inputType : undefined,
38665     
38666     /**
38667      * @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).
38668          */
38669         tabIndex : undefined,
38670         
38671     // private
38672     isFormField : true,
38673
38674     // private
38675     hasFocus : false,
38676     /**
38677      * @property {Roo.Element} fieldEl
38678      * Element Containing the rendered Field (with label etc.)
38679      */
38680     /**
38681      * @cfg {Mixed} value A value to initialize this field with.
38682      */
38683     value : undefined,
38684
38685     /**
38686      * @cfg {String} name The field's HTML name attribute.
38687      */
38688     /**
38689      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38690      */
38691     // private
38692     loadedValue : false,
38693      
38694      
38695         // private ??
38696         initComponent : function(){
38697         Roo.form.Field.superclass.initComponent.call(this);
38698         this.addEvents({
38699             /**
38700              * @event focus
38701              * Fires when this field receives input focus.
38702              * @param {Roo.form.Field} this
38703              */
38704             focus : true,
38705             /**
38706              * @event blur
38707              * Fires when this field loses input focus.
38708              * @param {Roo.form.Field} this
38709              */
38710             blur : true,
38711             /**
38712              * @event specialkey
38713              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38714              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38715              * @param {Roo.form.Field} this
38716              * @param {Roo.EventObject} e The event object
38717              */
38718             specialkey : true,
38719             /**
38720              * @event change
38721              * Fires just before the field blurs if the field value has changed.
38722              * @param {Roo.form.Field} this
38723              * @param {Mixed} newValue The new value
38724              * @param {Mixed} oldValue The original value
38725              */
38726             change : true,
38727             /**
38728              * @event invalid
38729              * Fires after the field has been marked as invalid.
38730              * @param {Roo.form.Field} this
38731              * @param {String} msg The validation message
38732              */
38733             invalid : true,
38734             /**
38735              * @event valid
38736              * Fires after the field has been validated with no errors.
38737              * @param {Roo.form.Field} this
38738              */
38739             valid : true,
38740              /**
38741              * @event keyup
38742              * Fires after the key up
38743              * @param {Roo.form.Field} this
38744              * @param {Roo.EventObject}  e The event Object
38745              */
38746             keyup : true
38747         });
38748     },
38749
38750     /**
38751      * Returns the name attribute of the field if available
38752      * @return {String} name The field name
38753      */
38754     getName: function(){
38755          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38756     },
38757
38758     // private
38759     onRender : function(ct, position){
38760         Roo.form.Field.superclass.onRender.call(this, ct, position);
38761         if(!this.el){
38762             var cfg = this.getAutoCreate();
38763             if(!cfg.name){
38764                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38765             }
38766             if (!cfg.name.length) {
38767                 delete cfg.name;
38768             }
38769             if(this.inputType){
38770                 cfg.type = this.inputType;
38771             }
38772             this.el = ct.createChild(cfg, position);
38773         }
38774         var type = this.el.dom.type;
38775         if(type){
38776             if(type == 'password'){
38777                 type = 'text';
38778             }
38779             this.el.addClass('x-form-'+type);
38780         }
38781         if(this.readOnly){
38782             this.el.dom.readOnly = true;
38783         }
38784         if(this.tabIndex !== undefined){
38785             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38786         }
38787
38788         this.el.addClass([this.fieldClass, this.cls]);
38789         this.initValue();
38790     },
38791
38792     /**
38793      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38794      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38795      * @return {Roo.form.Field} this
38796      */
38797     applyTo : function(target){
38798         this.allowDomMove = false;
38799         this.el = Roo.get(target);
38800         this.render(this.el.dom.parentNode);
38801         return this;
38802     },
38803
38804     // private
38805     initValue : function(){
38806         if(this.value !== undefined){
38807             this.setValue(this.value);
38808         }else if(this.el.dom.value.length > 0){
38809             this.setValue(this.el.dom.value);
38810         }
38811     },
38812
38813     /**
38814      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38815      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38816      */
38817     isDirty : function() {
38818         if(this.disabled) {
38819             return false;
38820         }
38821         return String(this.getValue()) !== String(this.originalValue);
38822     },
38823
38824     /**
38825      * stores the current value in loadedValue
38826      */
38827     resetHasChanged : function()
38828     {
38829         this.loadedValue = String(this.getValue());
38830     },
38831     /**
38832      * checks the current value against the 'loaded' value.
38833      * Note - will return false if 'resetHasChanged' has not been called first.
38834      */
38835     hasChanged : function()
38836     {
38837         if(this.disabled || this.readOnly) {
38838             return false;
38839         }
38840         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38841     },
38842     
38843     
38844     
38845     // private
38846     afterRender : function(){
38847         Roo.form.Field.superclass.afterRender.call(this);
38848         this.initEvents();
38849     },
38850
38851     // private
38852     fireKey : function(e){
38853         //Roo.log('field ' + e.getKey());
38854         if(e.isNavKeyPress()){
38855             this.fireEvent("specialkey", this, e);
38856         }
38857     },
38858
38859     /**
38860      * Resets the current field value to the originally loaded value and clears any validation messages
38861      */
38862     reset : function(){
38863         this.setValue(this.resetValue);
38864         this.clearInvalid();
38865     },
38866
38867     // private
38868     initEvents : function(){
38869         // safari killled keypress - so keydown is now used..
38870         this.el.on("keydown" , this.fireKey,  this);
38871         this.el.on("focus", this.onFocus,  this);
38872         this.el.on("blur", this.onBlur,  this);
38873         this.el.relayEvent('keyup', this);
38874
38875         // reference to original value for reset
38876         this.originalValue = this.getValue();
38877         this.resetValue =  this.getValue();
38878     },
38879
38880     // private
38881     onFocus : function(){
38882         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38883             this.el.addClass(this.focusClass);
38884         }
38885         if(!this.hasFocus){
38886             this.hasFocus = true;
38887             this.startValue = this.getValue();
38888             this.fireEvent("focus", this);
38889         }
38890     },
38891
38892     beforeBlur : Roo.emptyFn,
38893
38894     // private
38895     onBlur : function(){
38896         this.beforeBlur();
38897         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38898             this.el.removeClass(this.focusClass);
38899         }
38900         this.hasFocus = false;
38901         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38902             this.validate();
38903         }
38904         var v = this.getValue();
38905         if(String(v) !== String(this.startValue)){
38906             this.fireEvent('change', this, v, this.startValue);
38907         }
38908         this.fireEvent("blur", this);
38909     },
38910
38911     /**
38912      * Returns whether or not the field value is currently valid
38913      * @param {Boolean} preventMark True to disable marking the field invalid
38914      * @return {Boolean} True if the value is valid, else false
38915      */
38916     isValid : function(preventMark){
38917         if(this.disabled){
38918             return true;
38919         }
38920         var restore = this.preventMark;
38921         this.preventMark = preventMark === true;
38922         var v = this.validateValue(this.processValue(this.getRawValue()));
38923         this.preventMark = restore;
38924         return v;
38925     },
38926
38927     /**
38928      * Validates the field value
38929      * @return {Boolean} True if the value is valid, else false
38930      */
38931     validate : function(){
38932         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38933             this.clearInvalid();
38934             return true;
38935         }
38936         return false;
38937     },
38938
38939     processValue : function(value){
38940         return value;
38941     },
38942
38943     // private
38944     // Subclasses should provide the validation implementation by overriding this
38945     validateValue : function(value){
38946         return true;
38947     },
38948
38949     /**
38950      * Mark this field as invalid
38951      * @param {String} msg The validation message
38952      */
38953     markInvalid : function(msg){
38954         if(!this.rendered || this.preventMark){ // not rendered
38955             return;
38956         }
38957         
38958         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38959         
38960         obj.el.addClass(this.invalidClass);
38961         msg = msg || this.invalidText;
38962         switch(this.msgTarget){
38963             case 'qtip':
38964                 obj.el.dom.qtip = msg;
38965                 obj.el.dom.qclass = 'x-form-invalid-tip';
38966                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38967                     Roo.QuickTips.enable();
38968                 }
38969                 break;
38970             case 'title':
38971                 this.el.dom.title = msg;
38972                 break;
38973             case 'under':
38974                 if(!this.errorEl){
38975                     var elp = this.el.findParent('.x-form-element', 5, true);
38976                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38977                     this.errorEl.setWidth(elp.getWidth(true)-20);
38978                 }
38979                 this.errorEl.update(msg);
38980                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38981                 break;
38982             case 'side':
38983                 if(!this.errorIcon){
38984                     var elp = this.el.findParent('.x-form-element', 5, true);
38985                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38986                 }
38987                 this.alignErrorIcon();
38988                 this.errorIcon.dom.qtip = msg;
38989                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38990                 this.errorIcon.show();
38991                 this.on('resize', this.alignErrorIcon, this);
38992                 break;
38993             default:
38994                 var t = Roo.getDom(this.msgTarget);
38995                 t.innerHTML = msg;
38996                 t.style.display = this.msgDisplay;
38997                 break;
38998         }
38999         this.fireEvent('invalid', this, msg);
39000     },
39001
39002     // private
39003     alignErrorIcon : function(){
39004         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39005     },
39006
39007     /**
39008      * Clear any invalid styles/messages for this field
39009      */
39010     clearInvalid : function(){
39011         if(!this.rendered || this.preventMark){ // not rendered
39012             return;
39013         }
39014         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39015         
39016         obj.el.removeClass(this.invalidClass);
39017         switch(this.msgTarget){
39018             case 'qtip':
39019                 obj.el.dom.qtip = '';
39020                 break;
39021             case 'title':
39022                 this.el.dom.title = '';
39023                 break;
39024             case 'under':
39025                 if(this.errorEl){
39026                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39027                 }
39028                 break;
39029             case 'side':
39030                 if(this.errorIcon){
39031                     this.errorIcon.dom.qtip = '';
39032                     this.errorIcon.hide();
39033                     this.un('resize', this.alignErrorIcon, this);
39034                 }
39035                 break;
39036             default:
39037                 var t = Roo.getDom(this.msgTarget);
39038                 t.innerHTML = '';
39039                 t.style.display = 'none';
39040                 break;
39041         }
39042         this.fireEvent('valid', this);
39043     },
39044
39045     /**
39046      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39047      * @return {Mixed} value The field value
39048      */
39049     getRawValue : function(){
39050         var v = this.el.getValue();
39051         
39052         return v;
39053     },
39054
39055     /**
39056      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39057      * @return {Mixed} value The field value
39058      */
39059     getValue : function(){
39060         var v = this.el.getValue();
39061          
39062         return v;
39063     },
39064
39065     /**
39066      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39067      * @param {Mixed} value The value to set
39068      */
39069     setRawValue : function(v){
39070         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39071     },
39072
39073     /**
39074      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39075      * @param {Mixed} value The value to set
39076      */
39077     setValue : function(v){
39078         this.value = v;
39079         if(this.rendered){
39080             this.el.dom.value = (v === null || v === undefined ? '' : v);
39081              this.validate();
39082         }
39083     },
39084
39085     adjustSize : function(w, h){
39086         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39087         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39088         return s;
39089     },
39090
39091     adjustWidth : function(tag, w){
39092         tag = tag.toLowerCase();
39093         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39094             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39095                 if(tag == 'input'){
39096                     return w + 2;
39097                 }
39098                 if(tag == 'textarea'){
39099                     return w-2;
39100                 }
39101             }else if(Roo.isOpera){
39102                 if(tag == 'input'){
39103                     return w + 2;
39104                 }
39105                 if(tag == 'textarea'){
39106                     return w-2;
39107                 }
39108             }
39109         }
39110         return w;
39111     }
39112 });
39113
39114
39115 // anything other than normal should be considered experimental
39116 Roo.form.Field.msgFx = {
39117     normal : {
39118         show: function(msgEl, f){
39119             msgEl.setDisplayed('block');
39120         },
39121
39122         hide : function(msgEl, f){
39123             msgEl.setDisplayed(false).update('');
39124         }
39125     },
39126
39127     slide : {
39128         show: function(msgEl, f){
39129             msgEl.slideIn('t', {stopFx:true});
39130         },
39131
39132         hide : function(msgEl, f){
39133             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39134         }
39135     },
39136
39137     slideRight : {
39138         show: function(msgEl, f){
39139             msgEl.fixDisplay();
39140             msgEl.alignTo(f.el, 'tl-tr');
39141             msgEl.slideIn('l', {stopFx:true});
39142         },
39143
39144         hide : function(msgEl, f){
39145             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39146         }
39147     }
39148 };/*
39149  * Based on:
39150  * Ext JS Library 1.1.1
39151  * Copyright(c) 2006-2007, Ext JS, LLC.
39152  *
39153  * Originally Released Under LGPL - original licence link has changed is not relivant.
39154  *
39155  * Fork - LGPL
39156  * <script type="text/javascript">
39157  */
39158  
39159
39160 /**
39161  * @class Roo.form.TextField
39162  * @extends Roo.form.Field
39163  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39164  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39165  * @constructor
39166  * Creates a new TextField
39167  * @param {Object} config Configuration options
39168  */
39169 Roo.form.TextField = function(config){
39170     Roo.form.TextField.superclass.constructor.call(this, config);
39171     this.addEvents({
39172         /**
39173          * @event autosize
39174          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39175          * according to the default logic, but this event provides a hook for the developer to apply additional
39176          * logic at runtime to resize the field if needed.
39177              * @param {Roo.form.Field} this This text field
39178              * @param {Number} width The new field width
39179              */
39180         autosize : true
39181     });
39182 };
39183
39184 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39185     /**
39186      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39187      */
39188     grow : false,
39189     /**
39190      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39191      */
39192     growMin : 30,
39193     /**
39194      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39195      */
39196     growMax : 800,
39197     /**
39198      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39199      */
39200     vtype : null,
39201     /**
39202      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39203      */
39204     maskRe : null,
39205     /**
39206      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39207      */
39208     disableKeyFilter : false,
39209     /**
39210      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39211      */
39212     allowBlank : true,
39213     /**
39214      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39215      */
39216     minLength : 0,
39217     /**
39218      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39219      */
39220     maxLength : Number.MAX_VALUE,
39221     /**
39222      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39223      */
39224     minLengthText : "The minimum length for this field is {0}",
39225     /**
39226      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39227      */
39228     maxLengthText : "The maximum length for this field is {0}",
39229     /**
39230      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39231      */
39232     selectOnFocus : false,
39233     /**
39234      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39235      */
39236     blankText : "This field is required",
39237     /**
39238      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39239      * If available, this function will be called only after the basic validators all return true, and will be passed the
39240      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39241      */
39242     validator : null,
39243     /**
39244      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39245      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39246      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39247      */
39248     regex : null,
39249     /**
39250      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39251      */
39252     regexText : "",
39253     /**
39254      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39255      */
39256     emptyText : null,
39257    
39258
39259     // private
39260     initEvents : function()
39261     {
39262         if (this.emptyText) {
39263             this.el.attr('placeholder', this.emptyText);
39264         }
39265         
39266         Roo.form.TextField.superclass.initEvents.call(this);
39267         if(this.validationEvent == 'keyup'){
39268             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39269             this.el.on('keyup', this.filterValidation, this);
39270         }
39271         else if(this.validationEvent !== false){
39272             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39273         }
39274         
39275         if(this.selectOnFocus){
39276             this.on("focus", this.preFocus, this);
39277             
39278         }
39279         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39280             this.el.on("keypress", this.filterKeys, this);
39281         }
39282         if(this.grow){
39283             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39284             this.el.on("click", this.autoSize,  this);
39285         }
39286         if(this.el.is('input[type=password]') && Roo.isSafari){
39287             this.el.on('keydown', this.SafariOnKeyDown, this);
39288         }
39289     },
39290
39291     processValue : function(value){
39292         if(this.stripCharsRe){
39293             var newValue = value.replace(this.stripCharsRe, '');
39294             if(newValue !== value){
39295                 this.setRawValue(newValue);
39296                 return newValue;
39297             }
39298         }
39299         return value;
39300     },
39301
39302     filterValidation : function(e){
39303         if(!e.isNavKeyPress()){
39304             this.validationTask.delay(this.validationDelay);
39305         }
39306     },
39307
39308     // private
39309     onKeyUp : function(e){
39310         if(!e.isNavKeyPress()){
39311             this.autoSize();
39312         }
39313     },
39314
39315     /**
39316      * Resets the current field value to the originally-loaded value and clears any validation messages.
39317      *  
39318      */
39319     reset : function(){
39320         Roo.form.TextField.superclass.reset.call(this);
39321        
39322     },
39323
39324     
39325     // private
39326     preFocus : function(){
39327         
39328         if(this.selectOnFocus){
39329             this.el.dom.select();
39330         }
39331     },
39332
39333     
39334     // private
39335     filterKeys : function(e){
39336         var k = e.getKey();
39337         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39338             return;
39339         }
39340         var c = e.getCharCode(), cc = String.fromCharCode(c);
39341         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39342             return;
39343         }
39344         if(!this.maskRe.test(cc)){
39345             e.stopEvent();
39346         }
39347     },
39348
39349     setValue : function(v){
39350         
39351         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39352         
39353         this.autoSize();
39354     },
39355
39356     /**
39357      * Validates a value according to the field's validation rules and marks the field as invalid
39358      * if the validation fails
39359      * @param {Mixed} value The value to validate
39360      * @return {Boolean} True if the value is valid, else false
39361      */
39362     validateValue : function(value){
39363         if(value.length < 1)  { // if it's blank
39364              if(this.allowBlank){
39365                 this.clearInvalid();
39366                 return true;
39367              }else{
39368                 this.markInvalid(this.blankText);
39369                 return false;
39370              }
39371         }
39372         if(value.length < this.minLength){
39373             this.markInvalid(String.format(this.minLengthText, this.minLength));
39374             return false;
39375         }
39376         if(value.length > this.maxLength){
39377             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39378             return false;
39379         }
39380         if(this.vtype){
39381             var vt = Roo.form.VTypes;
39382             if(!vt[this.vtype](value, this)){
39383                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39384                 return false;
39385             }
39386         }
39387         if(typeof this.validator == "function"){
39388             var msg = this.validator(value);
39389             if(msg !== true){
39390                 this.markInvalid(msg);
39391                 return false;
39392             }
39393         }
39394         if(this.regex && !this.regex.test(value)){
39395             this.markInvalid(this.regexText);
39396             return false;
39397         }
39398         return true;
39399     },
39400
39401     /**
39402      * Selects text in this field
39403      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39404      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39405      */
39406     selectText : function(start, end){
39407         var v = this.getRawValue();
39408         if(v.length > 0){
39409             start = start === undefined ? 0 : start;
39410             end = end === undefined ? v.length : end;
39411             var d = this.el.dom;
39412             if(d.setSelectionRange){
39413                 d.setSelectionRange(start, end);
39414             }else if(d.createTextRange){
39415                 var range = d.createTextRange();
39416                 range.moveStart("character", start);
39417                 range.moveEnd("character", v.length-end);
39418                 range.select();
39419             }
39420         }
39421     },
39422
39423     /**
39424      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39425      * This only takes effect if grow = true, and fires the autosize event.
39426      */
39427     autoSize : function(){
39428         if(!this.grow || !this.rendered){
39429             return;
39430         }
39431         if(!this.metrics){
39432             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39433         }
39434         var el = this.el;
39435         var v = el.dom.value;
39436         var d = document.createElement('div');
39437         d.appendChild(document.createTextNode(v));
39438         v = d.innerHTML;
39439         d = null;
39440         v += "&#160;";
39441         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39442         this.el.setWidth(w);
39443         this.fireEvent("autosize", this, w);
39444     },
39445     
39446     // private
39447     SafariOnKeyDown : function(event)
39448     {
39449         // this is a workaround for a password hang bug on chrome/ webkit.
39450         
39451         var isSelectAll = false;
39452         
39453         if(this.el.dom.selectionEnd > 0){
39454             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39455         }
39456         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39457             event.preventDefault();
39458             this.setValue('');
39459             return;
39460         }
39461         
39462         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39463             
39464             event.preventDefault();
39465             // this is very hacky as keydown always get's upper case.
39466             
39467             var cc = String.fromCharCode(event.getCharCode());
39468             
39469             
39470             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39471             
39472         }
39473         
39474         
39475     }
39476 });/*
39477  * Based on:
39478  * Ext JS Library 1.1.1
39479  * Copyright(c) 2006-2007, Ext JS, LLC.
39480  *
39481  * Originally Released Under LGPL - original licence link has changed is not relivant.
39482  *
39483  * Fork - LGPL
39484  * <script type="text/javascript">
39485  */
39486  
39487 /**
39488  * @class Roo.form.Hidden
39489  * @extends Roo.form.TextField
39490  * Simple Hidden element used on forms 
39491  * 
39492  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39493  * 
39494  * @constructor
39495  * Creates a new Hidden form element.
39496  * @param {Object} config Configuration options
39497  */
39498
39499
39500
39501 // easy hidden field...
39502 Roo.form.Hidden = function(config){
39503     Roo.form.Hidden.superclass.constructor.call(this, config);
39504 };
39505   
39506 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39507     fieldLabel:      '',
39508     inputType:      'hidden',
39509     width:          50,
39510     allowBlank:     true,
39511     labelSeparator: '',
39512     hidden:         true,
39513     itemCls :       'x-form-item-display-none'
39514
39515
39516 });
39517
39518
39519 /*
39520  * Based on:
39521  * Ext JS Library 1.1.1
39522  * Copyright(c) 2006-2007, Ext JS, LLC.
39523  *
39524  * Originally Released Under LGPL - original licence link has changed is not relivant.
39525  *
39526  * Fork - LGPL
39527  * <script type="text/javascript">
39528  */
39529  
39530 /**
39531  * @class Roo.form.TriggerField
39532  * @extends Roo.form.TextField
39533  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39534  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39535  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39536  * for which you can provide a custom implementation.  For example:
39537  * <pre><code>
39538 var trigger = new Roo.form.TriggerField();
39539 trigger.onTriggerClick = myTriggerFn;
39540 trigger.applyTo('my-field');
39541 </code></pre>
39542  *
39543  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39544  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39545  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39546  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39547  * @constructor
39548  * Create a new TriggerField.
39549  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39550  * to the base TextField)
39551  */
39552 Roo.form.TriggerField = function(config){
39553     this.mimicing = false;
39554     Roo.form.TriggerField.superclass.constructor.call(this, config);
39555 };
39556
39557 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39558     /**
39559      * @cfg {String} triggerClass A CSS class to apply to the trigger
39560      */
39561     /**
39562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39563      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39564      */
39565     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39566     /**
39567      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39568      */
39569     hideTrigger:false,
39570
39571     /** @cfg {Boolean} grow @hide */
39572     /** @cfg {Number} growMin @hide */
39573     /** @cfg {Number} growMax @hide */
39574
39575     /**
39576      * @hide 
39577      * @method
39578      */
39579     autoSize: Roo.emptyFn,
39580     // private
39581     monitorTab : true,
39582     // private
39583     deferHeight : true,
39584
39585     
39586     actionMode : 'wrap',
39587     // private
39588     onResize : function(w, h){
39589         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39590         if(typeof w == 'number'){
39591             var x = w - this.trigger.getWidth();
39592             this.el.setWidth(this.adjustWidth('input', x));
39593             this.trigger.setStyle('left', x+'px');
39594         }
39595     },
39596
39597     // private
39598     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39599
39600     // private
39601     getResizeEl : function(){
39602         return this.wrap;
39603     },
39604
39605     // private
39606     getPositionEl : function(){
39607         return this.wrap;
39608     },
39609
39610     // private
39611     alignErrorIcon : function(){
39612         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39613     },
39614
39615     // private
39616     onRender : function(ct, position){
39617         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39618         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39619         this.trigger = this.wrap.createChild(this.triggerConfig ||
39620                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39621         if(this.hideTrigger){
39622             this.trigger.setDisplayed(false);
39623         }
39624         this.initTrigger();
39625         if(!this.width){
39626             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39627         }
39628     },
39629
39630     // private
39631     initTrigger : function(){
39632         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39633         this.trigger.addClassOnOver('x-form-trigger-over');
39634         this.trigger.addClassOnClick('x-form-trigger-click');
39635     },
39636
39637     // private
39638     onDestroy : function(){
39639         if(this.trigger){
39640             this.trigger.removeAllListeners();
39641             this.trigger.remove();
39642         }
39643         if(this.wrap){
39644             this.wrap.remove();
39645         }
39646         Roo.form.TriggerField.superclass.onDestroy.call(this);
39647     },
39648
39649     // private
39650     onFocus : function(){
39651         Roo.form.TriggerField.superclass.onFocus.call(this);
39652         if(!this.mimicing){
39653             this.wrap.addClass('x-trigger-wrap-focus');
39654             this.mimicing = true;
39655             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39656             if(this.monitorTab){
39657                 this.el.on("keydown", this.checkTab, this);
39658             }
39659         }
39660     },
39661
39662     // private
39663     checkTab : function(e){
39664         if(e.getKey() == e.TAB){
39665             this.triggerBlur();
39666         }
39667     },
39668
39669     // private
39670     onBlur : function(){
39671         // do nothing
39672     },
39673
39674     // private
39675     mimicBlur : function(e, t){
39676         if(!this.wrap.contains(t) && this.validateBlur()){
39677             this.triggerBlur();
39678         }
39679     },
39680
39681     // private
39682     triggerBlur : function(){
39683         this.mimicing = false;
39684         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39685         if(this.monitorTab){
39686             this.el.un("keydown", this.checkTab, this);
39687         }
39688         this.wrap.removeClass('x-trigger-wrap-focus');
39689         Roo.form.TriggerField.superclass.onBlur.call(this);
39690     },
39691
39692     // private
39693     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39694     validateBlur : function(e, t){
39695         return true;
39696     },
39697
39698     // private
39699     onDisable : function(){
39700         Roo.form.TriggerField.superclass.onDisable.call(this);
39701         if(this.wrap){
39702             this.wrap.addClass('x-item-disabled');
39703         }
39704     },
39705
39706     // private
39707     onEnable : function(){
39708         Roo.form.TriggerField.superclass.onEnable.call(this);
39709         if(this.wrap){
39710             this.wrap.removeClass('x-item-disabled');
39711         }
39712     },
39713
39714     // private
39715     onShow : function(){
39716         var ae = this.getActionEl();
39717         
39718         if(ae){
39719             ae.dom.style.display = '';
39720             ae.dom.style.visibility = 'visible';
39721         }
39722     },
39723
39724     // private
39725     
39726     onHide : function(){
39727         var ae = this.getActionEl();
39728         ae.dom.style.display = 'none';
39729     },
39730
39731     /**
39732      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39733      * by an implementing function.
39734      * @method
39735      * @param {EventObject} e
39736      */
39737     onTriggerClick : Roo.emptyFn
39738 });
39739
39740 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39741 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39742 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39743 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39744     initComponent : function(){
39745         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39746
39747         this.triggerConfig = {
39748             tag:'span', cls:'x-form-twin-triggers', cn:[
39749             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39750             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39751         ]};
39752     },
39753
39754     getTrigger : function(index){
39755         return this.triggers[index];
39756     },
39757
39758     initTrigger : function(){
39759         var ts = this.trigger.select('.x-form-trigger', true);
39760         this.wrap.setStyle('overflow', 'hidden');
39761         var triggerField = this;
39762         ts.each(function(t, all, index){
39763             t.hide = function(){
39764                 var w = triggerField.wrap.getWidth();
39765                 this.dom.style.display = 'none';
39766                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39767             };
39768             t.show = function(){
39769                 var w = triggerField.wrap.getWidth();
39770                 this.dom.style.display = '';
39771                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39772             };
39773             var triggerIndex = 'Trigger'+(index+1);
39774
39775             if(this['hide'+triggerIndex]){
39776                 t.dom.style.display = 'none';
39777             }
39778             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39779             t.addClassOnOver('x-form-trigger-over');
39780             t.addClassOnClick('x-form-trigger-click');
39781         }, this);
39782         this.triggers = ts.elements;
39783     },
39784
39785     onTrigger1Click : Roo.emptyFn,
39786     onTrigger2Click : Roo.emptyFn
39787 });/*
39788  * Based on:
39789  * Ext JS Library 1.1.1
39790  * Copyright(c) 2006-2007, Ext JS, LLC.
39791  *
39792  * Originally Released Under LGPL - original licence link has changed is not relivant.
39793  *
39794  * Fork - LGPL
39795  * <script type="text/javascript">
39796  */
39797  
39798 /**
39799  * @class Roo.form.TextArea
39800  * @extends Roo.form.TextField
39801  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39802  * support for auto-sizing.
39803  * @constructor
39804  * Creates a new TextArea
39805  * @param {Object} config Configuration options
39806  */
39807 Roo.form.TextArea = function(config){
39808     Roo.form.TextArea.superclass.constructor.call(this, config);
39809     // these are provided exchanges for backwards compat
39810     // minHeight/maxHeight were replaced by growMin/growMax to be
39811     // compatible with TextField growing config values
39812     if(this.minHeight !== undefined){
39813         this.growMin = this.minHeight;
39814     }
39815     if(this.maxHeight !== undefined){
39816         this.growMax = this.maxHeight;
39817     }
39818 };
39819
39820 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39821     /**
39822      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39823      */
39824     growMin : 60,
39825     /**
39826      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39827      */
39828     growMax: 1000,
39829     /**
39830      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39831      * in the field (equivalent to setting overflow: hidden, defaults to false)
39832      */
39833     preventScrollbars: false,
39834     /**
39835      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39836      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39837      */
39838
39839     // private
39840     onRender : function(ct, position){
39841         if(!this.el){
39842             this.defaultAutoCreate = {
39843                 tag: "textarea",
39844                 style:"width:300px;height:60px;",
39845                 autocomplete: "new-password"
39846             };
39847         }
39848         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39849         if(this.grow){
39850             this.textSizeEl = Roo.DomHelper.append(document.body, {
39851                 tag: "pre", cls: "x-form-grow-sizer"
39852             });
39853             if(this.preventScrollbars){
39854                 this.el.setStyle("overflow", "hidden");
39855             }
39856             this.el.setHeight(this.growMin);
39857         }
39858     },
39859
39860     onDestroy : function(){
39861         if(this.textSizeEl){
39862             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39863         }
39864         Roo.form.TextArea.superclass.onDestroy.call(this);
39865     },
39866
39867     // private
39868     onKeyUp : function(e){
39869         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39870             this.autoSize();
39871         }
39872     },
39873
39874     /**
39875      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39876      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39877      */
39878     autoSize : function(){
39879         if(!this.grow || !this.textSizeEl){
39880             return;
39881         }
39882         var el = this.el;
39883         var v = el.dom.value;
39884         var ts = this.textSizeEl;
39885
39886         ts.innerHTML = '';
39887         ts.appendChild(document.createTextNode(v));
39888         v = ts.innerHTML;
39889
39890         Roo.fly(ts).setWidth(this.el.getWidth());
39891         if(v.length < 1){
39892             v = "&#160;&#160;";
39893         }else{
39894             if(Roo.isIE){
39895                 v = v.replace(/\n/g, '<p>&#160;</p>');
39896             }
39897             v += "&#160;\n&#160;";
39898         }
39899         ts.innerHTML = v;
39900         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39901         if(h != this.lastHeight){
39902             this.lastHeight = h;
39903             this.el.setHeight(h);
39904             this.fireEvent("autosize", this, h);
39905         }
39906     }
39907 });/*
39908  * Based on:
39909  * Ext JS Library 1.1.1
39910  * Copyright(c) 2006-2007, Ext JS, LLC.
39911  *
39912  * Originally Released Under LGPL - original licence link has changed is not relivant.
39913  *
39914  * Fork - LGPL
39915  * <script type="text/javascript">
39916  */
39917  
39918
39919 /**
39920  * @class Roo.form.NumberField
39921  * @extends Roo.form.TextField
39922  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39923  * @constructor
39924  * Creates a new NumberField
39925  * @param {Object} config Configuration options
39926  */
39927 Roo.form.NumberField = function(config){
39928     Roo.form.NumberField.superclass.constructor.call(this, config);
39929 };
39930
39931 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39932     /**
39933      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39934      */
39935     fieldClass: "x-form-field x-form-num-field",
39936     /**
39937      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39938      */
39939     allowDecimals : true,
39940     /**
39941      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39942      */
39943     decimalSeparator : ".",
39944     /**
39945      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39946      */
39947     decimalPrecision : 2,
39948     /**
39949      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39950      */
39951     allowNegative : true,
39952     /**
39953      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39954      */
39955     minValue : Number.NEGATIVE_INFINITY,
39956     /**
39957      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39958      */
39959     maxValue : Number.MAX_VALUE,
39960     /**
39961      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39962      */
39963     minText : "The minimum value for this field is {0}",
39964     /**
39965      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39966      */
39967     maxText : "The maximum value for this field is {0}",
39968     /**
39969      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39970      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39971      */
39972     nanText : "{0} is not a valid number",
39973
39974     // private
39975     initEvents : function(){
39976         Roo.form.NumberField.superclass.initEvents.call(this);
39977         var allowed = "0123456789";
39978         if(this.allowDecimals){
39979             allowed += this.decimalSeparator;
39980         }
39981         if(this.allowNegative){
39982             allowed += "-";
39983         }
39984         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39985         var keyPress = function(e){
39986             var k = e.getKey();
39987             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39988                 return;
39989             }
39990             var c = e.getCharCode();
39991             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39992                 e.stopEvent();
39993             }
39994         };
39995         this.el.on("keypress", keyPress, this);
39996     },
39997
39998     // private
39999     validateValue : function(value){
40000         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40001             return false;
40002         }
40003         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40004              return true;
40005         }
40006         var num = this.parseValue(value);
40007         if(isNaN(num)){
40008             this.markInvalid(String.format(this.nanText, value));
40009             return false;
40010         }
40011         if(num < this.minValue){
40012             this.markInvalid(String.format(this.minText, this.minValue));
40013             return false;
40014         }
40015         if(num > this.maxValue){
40016             this.markInvalid(String.format(this.maxText, this.maxValue));
40017             return false;
40018         }
40019         return true;
40020     },
40021
40022     getValue : function(){
40023         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40024     },
40025
40026     // private
40027     parseValue : function(value){
40028         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40029         return isNaN(value) ? '' : value;
40030     },
40031
40032     // private
40033     fixPrecision : function(value){
40034         var nan = isNaN(value);
40035         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40036             return nan ? '' : value;
40037         }
40038         return parseFloat(value).toFixed(this.decimalPrecision);
40039     },
40040
40041     setValue : function(v){
40042         v = this.fixPrecision(v);
40043         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40044     },
40045
40046     // private
40047     decimalPrecisionFcn : function(v){
40048         return Math.floor(v);
40049     },
40050
40051     beforeBlur : function(){
40052         var v = this.parseValue(this.getRawValue());
40053         if(v){
40054             this.setValue(v);
40055         }
40056     }
40057 });/*
40058  * Based on:
40059  * Ext JS Library 1.1.1
40060  * Copyright(c) 2006-2007, Ext JS, LLC.
40061  *
40062  * Originally Released Under LGPL - original licence link has changed is not relivant.
40063  *
40064  * Fork - LGPL
40065  * <script type="text/javascript">
40066  */
40067  
40068 /**
40069  * @class Roo.form.DateField
40070  * @extends Roo.form.TriggerField
40071  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40072 * @constructor
40073 * Create a new DateField
40074 * @param {Object} config
40075  */
40076 Roo.form.DateField = function(config){
40077     Roo.form.DateField.superclass.constructor.call(this, config);
40078     
40079       this.addEvents({
40080          
40081         /**
40082          * @event select
40083          * Fires when a date is selected
40084              * @param {Roo.form.DateField} combo This combo box
40085              * @param {Date} date The date selected
40086              */
40087         'select' : true
40088          
40089     });
40090     
40091     
40092     if(typeof this.minValue == "string") {
40093         this.minValue = this.parseDate(this.minValue);
40094     }
40095     if(typeof this.maxValue == "string") {
40096         this.maxValue = this.parseDate(this.maxValue);
40097     }
40098     this.ddMatch = null;
40099     if(this.disabledDates){
40100         var dd = this.disabledDates;
40101         var re = "(?:";
40102         for(var i = 0; i < dd.length; i++){
40103             re += dd[i];
40104             if(i != dd.length-1) {
40105                 re += "|";
40106             }
40107         }
40108         this.ddMatch = new RegExp(re + ")");
40109     }
40110 };
40111
40112 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40113     /**
40114      * @cfg {String} format
40115      * The default date format string which can be overriden for localization support.  The format must be
40116      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40117      */
40118     format : "m/d/y",
40119     /**
40120      * @cfg {String} altFormats
40121      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40122      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40123      */
40124     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40125     /**
40126      * @cfg {Array} disabledDays
40127      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40128      */
40129     disabledDays : null,
40130     /**
40131      * @cfg {String} disabledDaysText
40132      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40133      */
40134     disabledDaysText : "Disabled",
40135     /**
40136      * @cfg {Array} disabledDates
40137      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40138      * expression so they are very powerful. Some examples:
40139      * <ul>
40140      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40141      * <li>["03/08", "09/16"] would disable those days for every year</li>
40142      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40143      * <li>["03/../2006"] would disable every day in March 2006</li>
40144      * <li>["^03"] would disable every day in every March</li>
40145      * </ul>
40146      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40147      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40148      */
40149     disabledDates : null,
40150     /**
40151      * @cfg {String} disabledDatesText
40152      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40153      */
40154     disabledDatesText : "Disabled",
40155     /**
40156      * @cfg {Date/String} minValue
40157      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40158      * valid format (defaults to null).
40159      */
40160     minValue : null,
40161     /**
40162      * @cfg {Date/String} maxValue
40163      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40164      * valid format (defaults to null).
40165      */
40166     maxValue : null,
40167     /**
40168      * @cfg {String} minText
40169      * The error text to display when the date in the cell is before minValue (defaults to
40170      * 'The date in this field must be after {minValue}').
40171      */
40172     minText : "The date in this field must be equal to or after {0}",
40173     /**
40174      * @cfg {String} maxText
40175      * The error text to display when the date in the cell is after maxValue (defaults to
40176      * 'The date in this field must be before {maxValue}').
40177      */
40178     maxText : "The date in this field must be equal to or before {0}",
40179     /**
40180      * @cfg {String} invalidText
40181      * The error text to display when the date in the field is invalid (defaults to
40182      * '{value} is not a valid date - it must be in the format {format}').
40183      */
40184     invalidText : "{0} is not a valid date - it must be in the format {1}",
40185     /**
40186      * @cfg {String} triggerClass
40187      * An additional CSS class used to style the trigger button.  The trigger will always get the
40188      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40189      * which displays a calendar icon).
40190      */
40191     triggerClass : 'x-form-date-trigger',
40192     
40193
40194     /**
40195      * @cfg {Boolean} useIso
40196      * if enabled, then the date field will use a hidden field to store the 
40197      * real value as iso formated date. default (false)
40198      */ 
40199     useIso : false,
40200     /**
40201      * @cfg {String/Object} autoCreate
40202      * A DomHelper element spec, or true for a default element spec (defaults to
40203      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40204      */ 
40205     // private
40206     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40207     
40208     // private
40209     hiddenField: false,
40210     
40211     onRender : function(ct, position)
40212     {
40213         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40214         if (this.useIso) {
40215             //this.el.dom.removeAttribute('name'); 
40216             Roo.log("Changing name?");
40217             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40218             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40219                     'before', true);
40220             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40221             // prevent input submission
40222             this.hiddenName = this.name;
40223         }
40224             
40225             
40226     },
40227     
40228     // private
40229     validateValue : function(value)
40230     {
40231         value = this.formatDate(value);
40232         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40233             Roo.log('super failed');
40234             return false;
40235         }
40236         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40237              return true;
40238         }
40239         var svalue = value;
40240         value = this.parseDate(value);
40241         if(!value){
40242             Roo.log('parse date failed' + svalue);
40243             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40244             return false;
40245         }
40246         var time = value.getTime();
40247         if(this.minValue && time < this.minValue.getTime()){
40248             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40249             return false;
40250         }
40251         if(this.maxValue && time > this.maxValue.getTime()){
40252             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40253             return false;
40254         }
40255         if(this.disabledDays){
40256             var day = value.getDay();
40257             for(var i = 0; i < this.disabledDays.length; i++) {
40258                 if(day === this.disabledDays[i]){
40259                     this.markInvalid(this.disabledDaysText);
40260                     return false;
40261                 }
40262             }
40263         }
40264         var fvalue = this.formatDate(value);
40265         if(this.ddMatch && this.ddMatch.test(fvalue)){
40266             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40267             return false;
40268         }
40269         return true;
40270     },
40271
40272     // private
40273     // Provides logic to override the default TriggerField.validateBlur which just returns true
40274     validateBlur : function(){
40275         return !this.menu || !this.menu.isVisible();
40276     },
40277     
40278     getName: function()
40279     {
40280         // returns hidden if it's set..
40281         if (!this.rendered) {return ''};
40282         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40283         
40284     },
40285
40286     /**
40287      * Returns the current date value of the date field.
40288      * @return {Date} The date value
40289      */
40290     getValue : function(){
40291         
40292         return  this.hiddenField ?
40293                 this.hiddenField.value :
40294                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40295     },
40296
40297     /**
40298      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40299      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40300      * (the default format used is "m/d/y").
40301      * <br />Usage:
40302      * <pre><code>
40303 //All of these calls set the same date value (May 4, 2006)
40304
40305 //Pass a date object:
40306 var dt = new Date('5/4/06');
40307 dateField.setValue(dt);
40308
40309 //Pass a date string (default format):
40310 dateField.setValue('5/4/06');
40311
40312 //Pass a date string (custom format):
40313 dateField.format = 'Y-m-d';
40314 dateField.setValue('2006-5-4');
40315 </code></pre>
40316      * @param {String/Date} date The date or valid date string
40317      */
40318     setValue : function(date){
40319         if (this.hiddenField) {
40320             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40321         }
40322         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40323         // make sure the value field is always stored as a date..
40324         this.value = this.parseDate(date);
40325         
40326         
40327     },
40328
40329     // private
40330     parseDate : function(value){
40331         if(!value || value instanceof Date){
40332             return value;
40333         }
40334         var v = Date.parseDate(value, this.format);
40335          if (!v && this.useIso) {
40336             v = Date.parseDate(value, 'Y-m-d');
40337         }
40338         if(!v && this.altFormats){
40339             if(!this.altFormatsArray){
40340                 this.altFormatsArray = this.altFormats.split("|");
40341             }
40342             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40343                 v = Date.parseDate(value, this.altFormatsArray[i]);
40344             }
40345         }
40346         return v;
40347     },
40348
40349     // private
40350     formatDate : function(date, fmt){
40351         return (!date || !(date instanceof Date)) ?
40352                date : date.dateFormat(fmt || this.format);
40353     },
40354
40355     // private
40356     menuListeners : {
40357         select: function(m, d){
40358             
40359             this.setValue(d);
40360             this.fireEvent('select', this, d);
40361         },
40362         show : function(){ // retain focus styling
40363             this.onFocus();
40364         },
40365         hide : function(){
40366             this.focus.defer(10, this);
40367             var ml = this.menuListeners;
40368             this.menu.un("select", ml.select,  this);
40369             this.menu.un("show", ml.show,  this);
40370             this.menu.un("hide", ml.hide,  this);
40371         }
40372     },
40373
40374     // private
40375     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40376     onTriggerClick : function(){
40377         if(this.disabled){
40378             return;
40379         }
40380         if(this.menu == null){
40381             this.menu = new Roo.menu.DateMenu();
40382         }
40383         Roo.apply(this.menu.picker,  {
40384             showClear: this.allowBlank,
40385             minDate : this.minValue,
40386             maxDate : this.maxValue,
40387             disabledDatesRE : this.ddMatch,
40388             disabledDatesText : this.disabledDatesText,
40389             disabledDays : this.disabledDays,
40390             disabledDaysText : this.disabledDaysText,
40391             format : this.useIso ? 'Y-m-d' : this.format,
40392             minText : String.format(this.minText, this.formatDate(this.minValue)),
40393             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40394         });
40395         this.menu.on(Roo.apply({}, this.menuListeners, {
40396             scope:this
40397         }));
40398         this.menu.picker.setValue(this.getValue() || new Date());
40399         this.menu.show(this.el, "tl-bl?");
40400     },
40401
40402     beforeBlur : function(){
40403         var v = this.parseDate(this.getRawValue());
40404         if(v){
40405             this.setValue(v);
40406         }
40407     },
40408
40409     /*@
40410      * overide
40411      * 
40412      */
40413     isDirty : function() {
40414         if(this.disabled) {
40415             return false;
40416         }
40417         
40418         if(typeof(this.startValue) === 'undefined'){
40419             return false;
40420         }
40421         
40422         return String(this.getValue()) !== String(this.startValue);
40423         
40424     }
40425 });/*
40426  * Based on:
40427  * Ext JS Library 1.1.1
40428  * Copyright(c) 2006-2007, Ext JS, LLC.
40429  *
40430  * Originally Released Under LGPL - original licence link has changed is not relivant.
40431  *
40432  * Fork - LGPL
40433  * <script type="text/javascript">
40434  */
40435  
40436 /**
40437  * @class Roo.form.MonthField
40438  * @extends Roo.form.TriggerField
40439  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40440 * @constructor
40441 * Create a new MonthField
40442 * @param {Object} config
40443  */
40444 Roo.form.MonthField = function(config){
40445     
40446     Roo.form.MonthField.superclass.constructor.call(this, config);
40447     
40448       this.addEvents({
40449          
40450         /**
40451          * @event select
40452          * Fires when a date is selected
40453              * @param {Roo.form.MonthFieeld} combo This combo box
40454              * @param {Date} date The date selected
40455              */
40456         'select' : true
40457          
40458     });
40459     
40460     
40461     if(typeof this.minValue == "string") {
40462         this.minValue = this.parseDate(this.minValue);
40463     }
40464     if(typeof this.maxValue == "string") {
40465         this.maxValue = this.parseDate(this.maxValue);
40466     }
40467     this.ddMatch = null;
40468     if(this.disabledDates){
40469         var dd = this.disabledDates;
40470         var re = "(?:";
40471         for(var i = 0; i < dd.length; i++){
40472             re += dd[i];
40473             if(i != dd.length-1) {
40474                 re += "|";
40475             }
40476         }
40477         this.ddMatch = new RegExp(re + ")");
40478     }
40479 };
40480
40481 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40482     /**
40483      * @cfg {String} format
40484      * The default date format string which can be overriden for localization support.  The format must be
40485      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40486      */
40487     format : "M Y",
40488     /**
40489      * @cfg {String} altFormats
40490      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40491      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40492      */
40493     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40494     /**
40495      * @cfg {Array} disabledDays
40496      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40497      */
40498     disabledDays : [0,1,2,3,4,5,6],
40499     /**
40500      * @cfg {String} disabledDaysText
40501      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40502      */
40503     disabledDaysText : "Disabled",
40504     /**
40505      * @cfg {Array} disabledDates
40506      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40507      * expression so they are very powerful. Some examples:
40508      * <ul>
40509      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40510      * <li>["03/08", "09/16"] would disable those days for every year</li>
40511      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40512      * <li>["03/../2006"] would disable every day in March 2006</li>
40513      * <li>["^03"] would disable every day in every March</li>
40514      * </ul>
40515      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40516      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40517      */
40518     disabledDates : null,
40519     /**
40520      * @cfg {String} disabledDatesText
40521      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40522      */
40523     disabledDatesText : "Disabled",
40524     /**
40525      * @cfg {Date/String} minValue
40526      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40527      * valid format (defaults to null).
40528      */
40529     minValue : null,
40530     /**
40531      * @cfg {Date/String} maxValue
40532      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40533      * valid format (defaults to null).
40534      */
40535     maxValue : null,
40536     /**
40537      * @cfg {String} minText
40538      * The error text to display when the date in the cell is before minValue (defaults to
40539      * 'The date in this field must be after {minValue}').
40540      */
40541     minText : "The date in this field must be equal to or after {0}",
40542     /**
40543      * @cfg {String} maxTextf
40544      * The error text to display when the date in the cell is after maxValue (defaults to
40545      * 'The date in this field must be before {maxValue}').
40546      */
40547     maxText : "The date in this field must be equal to or before {0}",
40548     /**
40549      * @cfg {String} invalidText
40550      * The error text to display when the date in the field is invalid (defaults to
40551      * '{value} is not a valid date - it must be in the format {format}').
40552      */
40553     invalidText : "{0} is not a valid date - it must be in the format {1}",
40554     /**
40555      * @cfg {String} triggerClass
40556      * An additional CSS class used to style the trigger button.  The trigger will always get the
40557      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40558      * which displays a calendar icon).
40559      */
40560     triggerClass : 'x-form-date-trigger',
40561     
40562
40563     /**
40564      * @cfg {Boolean} useIso
40565      * if enabled, then the date field will use a hidden field to store the 
40566      * real value as iso formated date. default (true)
40567      */ 
40568     useIso : true,
40569     /**
40570      * @cfg {String/Object} autoCreate
40571      * A DomHelper element spec, or true for a default element spec (defaults to
40572      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40573      */ 
40574     // private
40575     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40576     
40577     // private
40578     hiddenField: false,
40579     
40580     hideMonthPicker : false,
40581     
40582     onRender : function(ct, position)
40583     {
40584         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40585         if (this.useIso) {
40586             this.el.dom.removeAttribute('name'); 
40587             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40588                     'before', true);
40589             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40590             // prevent input submission
40591             this.hiddenName = this.name;
40592         }
40593             
40594             
40595     },
40596     
40597     // private
40598     validateValue : function(value)
40599     {
40600         value = this.formatDate(value);
40601         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40602             return false;
40603         }
40604         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40605              return true;
40606         }
40607         var svalue = value;
40608         value = this.parseDate(value);
40609         if(!value){
40610             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40611             return false;
40612         }
40613         var time = value.getTime();
40614         if(this.minValue && time < this.minValue.getTime()){
40615             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40616             return false;
40617         }
40618         if(this.maxValue && time > this.maxValue.getTime()){
40619             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40620             return false;
40621         }
40622         /*if(this.disabledDays){
40623             var day = value.getDay();
40624             for(var i = 0; i < this.disabledDays.length; i++) {
40625                 if(day === this.disabledDays[i]){
40626                     this.markInvalid(this.disabledDaysText);
40627                     return false;
40628                 }
40629             }
40630         }
40631         */
40632         var fvalue = this.formatDate(value);
40633         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40634             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40635             return false;
40636         }
40637         */
40638         return true;
40639     },
40640
40641     // private
40642     // Provides logic to override the default TriggerField.validateBlur which just returns true
40643     validateBlur : function(){
40644         return !this.menu || !this.menu.isVisible();
40645     },
40646
40647     /**
40648      * Returns the current date value of the date field.
40649      * @return {Date} The date value
40650      */
40651     getValue : function(){
40652         
40653         
40654         
40655         return  this.hiddenField ?
40656                 this.hiddenField.value :
40657                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40658     },
40659
40660     /**
40661      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40662      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40663      * (the default format used is "m/d/y").
40664      * <br />Usage:
40665      * <pre><code>
40666 //All of these calls set the same date value (May 4, 2006)
40667
40668 //Pass a date object:
40669 var dt = new Date('5/4/06');
40670 monthField.setValue(dt);
40671
40672 //Pass a date string (default format):
40673 monthField.setValue('5/4/06');
40674
40675 //Pass a date string (custom format):
40676 monthField.format = 'Y-m-d';
40677 monthField.setValue('2006-5-4');
40678 </code></pre>
40679      * @param {String/Date} date The date or valid date string
40680      */
40681     setValue : function(date){
40682         Roo.log('month setValue' + date);
40683         // can only be first of month..
40684         
40685         var val = this.parseDate(date);
40686         
40687         if (this.hiddenField) {
40688             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40689         }
40690         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40691         this.value = this.parseDate(date);
40692     },
40693
40694     // private
40695     parseDate : function(value){
40696         if(!value || value instanceof Date){
40697             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40698             return value;
40699         }
40700         var v = Date.parseDate(value, this.format);
40701         if (!v && this.useIso) {
40702             v = Date.parseDate(value, 'Y-m-d');
40703         }
40704         if (v) {
40705             // 
40706             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40707         }
40708         
40709         
40710         if(!v && this.altFormats){
40711             if(!this.altFormatsArray){
40712                 this.altFormatsArray = this.altFormats.split("|");
40713             }
40714             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40715                 v = Date.parseDate(value, this.altFormatsArray[i]);
40716             }
40717         }
40718         return v;
40719     },
40720
40721     // private
40722     formatDate : function(date, fmt){
40723         return (!date || !(date instanceof Date)) ?
40724                date : date.dateFormat(fmt || this.format);
40725     },
40726
40727     // private
40728     menuListeners : {
40729         select: function(m, d){
40730             this.setValue(d);
40731             this.fireEvent('select', this, d);
40732         },
40733         show : function(){ // retain focus styling
40734             this.onFocus();
40735         },
40736         hide : function(){
40737             this.focus.defer(10, this);
40738             var ml = this.menuListeners;
40739             this.menu.un("select", ml.select,  this);
40740             this.menu.un("show", ml.show,  this);
40741             this.menu.un("hide", ml.hide,  this);
40742         }
40743     },
40744     // private
40745     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40746     onTriggerClick : function(){
40747         if(this.disabled){
40748             return;
40749         }
40750         if(this.menu == null){
40751             this.menu = new Roo.menu.DateMenu();
40752            
40753         }
40754         
40755         Roo.apply(this.menu.picker,  {
40756             
40757             showClear: this.allowBlank,
40758             minDate : this.minValue,
40759             maxDate : this.maxValue,
40760             disabledDatesRE : this.ddMatch,
40761             disabledDatesText : this.disabledDatesText,
40762             
40763             format : this.useIso ? 'Y-m-d' : this.format,
40764             minText : String.format(this.minText, this.formatDate(this.minValue)),
40765             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40766             
40767         });
40768          this.menu.on(Roo.apply({}, this.menuListeners, {
40769             scope:this
40770         }));
40771        
40772         
40773         var m = this.menu;
40774         var p = m.picker;
40775         
40776         // hide month picker get's called when we called by 'before hide';
40777         
40778         var ignorehide = true;
40779         p.hideMonthPicker  = function(disableAnim){
40780             if (ignorehide) {
40781                 return;
40782             }
40783              if(this.monthPicker){
40784                 Roo.log("hideMonthPicker called");
40785                 if(disableAnim === true){
40786                     this.monthPicker.hide();
40787                 }else{
40788                     this.monthPicker.slideOut('t', {duration:.2});
40789                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40790                     p.fireEvent("select", this, this.value);
40791                     m.hide();
40792                 }
40793             }
40794         }
40795         
40796         Roo.log('picker set value');
40797         Roo.log(this.getValue());
40798         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40799         m.show(this.el, 'tl-bl?');
40800         ignorehide  = false;
40801         // this will trigger hideMonthPicker..
40802         
40803         
40804         // hidden the day picker
40805         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40806         
40807         
40808         
40809       
40810         
40811         p.showMonthPicker.defer(100, p);
40812     
40813         
40814        
40815     },
40816
40817     beforeBlur : function(){
40818         var v = this.parseDate(this.getRawValue());
40819         if(v){
40820             this.setValue(v);
40821         }
40822     }
40823
40824     /** @cfg {Boolean} grow @hide */
40825     /** @cfg {Number} growMin @hide */
40826     /** @cfg {Number} growMax @hide */
40827     /**
40828      * @hide
40829      * @method autoSize
40830      */
40831 });/*
40832  * Based on:
40833  * Ext JS Library 1.1.1
40834  * Copyright(c) 2006-2007, Ext JS, LLC.
40835  *
40836  * Originally Released Under LGPL - original licence link has changed is not relivant.
40837  *
40838  * Fork - LGPL
40839  * <script type="text/javascript">
40840  */
40841  
40842
40843 /**
40844  * @class Roo.form.ComboBox
40845  * @extends Roo.form.TriggerField
40846  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40847  * @constructor
40848  * Create a new ComboBox.
40849  * @param {Object} config Configuration options
40850  */
40851 Roo.form.ComboBox = function(config){
40852     Roo.form.ComboBox.superclass.constructor.call(this, config);
40853     this.addEvents({
40854         /**
40855          * @event expand
40856          * Fires when the dropdown list is expanded
40857              * @param {Roo.form.ComboBox} combo This combo box
40858              */
40859         'expand' : true,
40860         /**
40861          * @event collapse
40862          * Fires when the dropdown list is collapsed
40863              * @param {Roo.form.ComboBox} combo This combo box
40864              */
40865         'collapse' : true,
40866         /**
40867          * @event beforeselect
40868          * Fires before a list item is selected. Return false to cancel the selection.
40869              * @param {Roo.form.ComboBox} combo This combo box
40870              * @param {Roo.data.Record} record The data record returned from the underlying store
40871              * @param {Number} index The index of the selected item in the dropdown list
40872              */
40873         'beforeselect' : true,
40874         /**
40875          * @event select
40876          * Fires when a list item is selected
40877              * @param {Roo.form.ComboBox} combo This combo box
40878              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40879              * @param {Number} index The index of the selected item in the dropdown list
40880              */
40881         'select' : true,
40882         /**
40883          * @event beforequery
40884          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40885          * The event object passed has these properties:
40886              * @param {Roo.form.ComboBox} combo This combo box
40887              * @param {String} query The query
40888              * @param {Boolean} forceAll true to force "all" query
40889              * @param {Boolean} cancel true to cancel the query
40890              * @param {Object} e The query event object
40891              */
40892         'beforequery': true,
40893          /**
40894          * @event add
40895          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40896              * @param {Roo.form.ComboBox} combo This combo box
40897              */
40898         'add' : true,
40899         /**
40900          * @event edit
40901          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40902              * @param {Roo.form.ComboBox} combo This combo box
40903              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40904              */
40905         'edit' : true
40906         
40907         
40908     });
40909     if(this.transform){
40910         this.allowDomMove = false;
40911         var s = Roo.getDom(this.transform);
40912         if(!this.hiddenName){
40913             this.hiddenName = s.name;
40914         }
40915         if(!this.store){
40916             this.mode = 'local';
40917             var d = [], opts = s.options;
40918             for(var i = 0, len = opts.length;i < len; i++){
40919                 var o = opts[i];
40920                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40921                 if(o.selected) {
40922                     this.value = value;
40923                 }
40924                 d.push([value, o.text]);
40925             }
40926             this.store = new Roo.data.SimpleStore({
40927                 'id': 0,
40928                 fields: ['value', 'text'],
40929                 data : d
40930             });
40931             this.valueField = 'value';
40932             this.displayField = 'text';
40933         }
40934         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40935         if(!this.lazyRender){
40936             this.target = true;
40937             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40938             s.parentNode.removeChild(s); // remove it
40939             this.render(this.el.parentNode);
40940         }else{
40941             s.parentNode.removeChild(s); // remove it
40942         }
40943
40944     }
40945     if (this.store) {
40946         this.store = Roo.factory(this.store, Roo.data);
40947     }
40948     
40949     this.selectedIndex = -1;
40950     if(this.mode == 'local'){
40951         if(config.queryDelay === undefined){
40952             this.queryDelay = 10;
40953         }
40954         if(config.minChars === undefined){
40955             this.minChars = 0;
40956         }
40957     }
40958 };
40959
40960 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40961     /**
40962      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40963      */
40964     /**
40965      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40966      * rendering into an Roo.Editor, defaults to false)
40967      */
40968     /**
40969      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40970      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40971      */
40972     /**
40973      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40974      */
40975     /**
40976      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40977      * the dropdown list (defaults to undefined, with no header element)
40978      */
40979
40980      /**
40981      * @cfg {String/Roo.Template} tpl The template to use to render the output
40982      */
40983      
40984     // private
40985     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40986     /**
40987      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40988      */
40989     listWidth: undefined,
40990     /**
40991      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40992      * mode = 'remote' or 'text' if mode = 'local')
40993      */
40994     displayField: undefined,
40995     /**
40996      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40997      * mode = 'remote' or 'value' if mode = 'local'). 
40998      * Note: use of a valueField requires the user make a selection
40999      * in order for a value to be mapped.
41000      */
41001     valueField: undefined,
41002     
41003     
41004     /**
41005      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41006      * field's data value (defaults to the underlying DOM element's name)
41007      */
41008     hiddenName: undefined,
41009     /**
41010      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41011      */
41012     listClass: '',
41013     /**
41014      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41015      */
41016     selectedClass: 'x-combo-selected',
41017     /**
41018      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41019      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41020      * which displays a downward arrow icon).
41021      */
41022     triggerClass : 'x-form-arrow-trigger',
41023     /**
41024      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41025      */
41026     shadow:'sides',
41027     /**
41028      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41029      * anchor positions (defaults to 'tl-bl')
41030      */
41031     listAlign: 'tl-bl?',
41032     /**
41033      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41034      */
41035     maxHeight: 300,
41036     /**
41037      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41038      * query specified by the allQuery config option (defaults to 'query')
41039      */
41040     triggerAction: 'query',
41041     /**
41042      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41043      * (defaults to 4, does not apply if editable = false)
41044      */
41045     minChars : 4,
41046     /**
41047      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41048      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41049      */
41050     typeAhead: false,
41051     /**
41052      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41053      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41054      */
41055     queryDelay: 500,
41056     /**
41057      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41058      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41059      */
41060     pageSize: 0,
41061     /**
41062      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41063      * when editable = true (defaults to false)
41064      */
41065     selectOnFocus:false,
41066     /**
41067      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41068      */
41069     queryParam: 'query',
41070     /**
41071      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41072      * when mode = 'remote' (defaults to 'Loading...')
41073      */
41074     loadingText: 'Loading...',
41075     /**
41076      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41077      */
41078     resizable: false,
41079     /**
41080      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41081      */
41082     handleHeight : 8,
41083     /**
41084      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41085      * traditional select (defaults to true)
41086      */
41087     editable: true,
41088     /**
41089      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41090      */
41091     allQuery: '',
41092     /**
41093      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41094      */
41095     mode: 'remote',
41096     /**
41097      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41098      * listWidth has a higher value)
41099      */
41100     minListWidth : 70,
41101     /**
41102      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41103      * allow the user to set arbitrary text into the field (defaults to false)
41104      */
41105     forceSelection:false,
41106     /**
41107      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41108      * if typeAhead = true (defaults to 250)
41109      */
41110     typeAheadDelay : 250,
41111     /**
41112      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41113      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41114      */
41115     valueNotFoundText : undefined,
41116     /**
41117      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41118      */
41119     blockFocus : false,
41120     
41121     /**
41122      * @cfg {Boolean} disableClear Disable showing of clear button.
41123      */
41124     disableClear : false,
41125     /**
41126      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41127      */
41128     alwaysQuery : false,
41129     
41130     //private
41131     addicon : false,
41132     editicon: false,
41133     
41134     // element that contains real text value.. (when hidden is used..)
41135      
41136     // private
41137     onRender : function(ct, position){
41138         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41139         if(this.hiddenName){
41140             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41141                     'before', true);
41142             this.hiddenField.value =
41143                 this.hiddenValue !== undefined ? this.hiddenValue :
41144                 this.value !== undefined ? this.value : '';
41145
41146             // prevent input submission
41147             this.el.dom.removeAttribute('name');
41148              
41149              
41150         }
41151         if(Roo.isGecko){
41152             this.el.dom.setAttribute('autocomplete', 'off');
41153         }
41154
41155         var cls = 'x-combo-list';
41156
41157         this.list = new Roo.Layer({
41158             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41159         });
41160
41161         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41162         this.list.setWidth(lw);
41163         this.list.swallowEvent('mousewheel');
41164         this.assetHeight = 0;
41165
41166         if(this.title){
41167             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41168             this.assetHeight += this.header.getHeight();
41169         }
41170
41171         this.innerList = this.list.createChild({cls:cls+'-inner'});
41172         this.innerList.on('mouseover', this.onViewOver, this);
41173         this.innerList.on('mousemove', this.onViewMove, this);
41174         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41175         
41176         if(this.allowBlank && !this.pageSize && !this.disableClear){
41177             this.footer = this.list.createChild({cls:cls+'-ft'});
41178             this.pageTb = new Roo.Toolbar(this.footer);
41179            
41180         }
41181         if(this.pageSize){
41182             this.footer = this.list.createChild({cls:cls+'-ft'});
41183             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41184                     {pageSize: this.pageSize});
41185             
41186         }
41187         
41188         if (this.pageTb && this.allowBlank && !this.disableClear) {
41189             var _this = this;
41190             this.pageTb.add(new Roo.Toolbar.Fill(), {
41191                 cls: 'x-btn-icon x-btn-clear',
41192                 text: '&#160;',
41193                 handler: function()
41194                 {
41195                     _this.collapse();
41196                     _this.clearValue();
41197                     _this.onSelect(false, -1);
41198                 }
41199             });
41200         }
41201         if (this.footer) {
41202             this.assetHeight += this.footer.getHeight();
41203         }
41204         
41205
41206         if(!this.tpl){
41207             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41208         }
41209
41210         this.view = new Roo.View(this.innerList, this.tpl, {
41211             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41212         });
41213
41214         this.view.on('click', this.onViewClick, this);
41215
41216         this.store.on('beforeload', this.onBeforeLoad, this);
41217         this.store.on('load', this.onLoad, this);
41218         this.store.on('loadexception', this.onLoadException, this);
41219
41220         if(this.resizable){
41221             this.resizer = new Roo.Resizable(this.list,  {
41222                pinned:true, handles:'se'
41223             });
41224             this.resizer.on('resize', function(r, w, h){
41225                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41226                 this.listWidth = w;
41227                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41228                 this.restrictHeight();
41229             }, this);
41230             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41231         }
41232         if(!this.editable){
41233             this.editable = true;
41234             this.setEditable(false);
41235         }  
41236         
41237         
41238         if (typeof(this.events.add.listeners) != 'undefined') {
41239             
41240             this.addicon = this.wrap.createChild(
41241                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41242        
41243             this.addicon.on('click', function(e) {
41244                 this.fireEvent('add', this);
41245             }, this);
41246         }
41247         if (typeof(this.events.edit.listeners) != 'undefined') {
41248             
41249             this.editicon = this.wrap.createChild(
41250                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41251             if (this.addicon) {
41252                 this.editicon.setStyle('margin-left', '40px');
41253             }
41254             this.editicon.on('click', function(e) {
41255                 
41256                 // we fire even  if inothing is selected..
41257                 this.fireEvent('edit', this, this.lastData );
41258                 
41259             }, this);
41260         }
41261         
41262         
41263         
41264     },
41265
41266     // private
41267     initEvents : function(){
41268         Roo.form.ComboBox.superclass.initEvents.call(this);
41269
41270         this.keyNav = new Roo.KeyNav(this.el, {
41271             "up" : function(e){
41272                 this.inKeyMode = true;
41273                 this.selectPrev();
41274             },
41275
41276             "down" : function(e){
41277                 if(!this.isExpanded()){
41278                     this.onTriggerClick();
41279                 }else{
41280                     this.inKeyMode = true;
41281                     this.selectNext();
41282                 }
41283             },
41284
41285             "enter" : function(e){
41286                 this.onViewClick();
41287                 //return true;
41288             },
41289
41290             "esc" : function(e){
41291                 this.collapse();
41292             },
41293
41294             "tab" : function(e){
41295                 this.onViewClick(false);
41296                 this.fireEvent("specialkey", this, e);
41297                 return true;
41298             },
41299
41300             scope : this,
41301
41302             doRelay : function(foo, bar, hname){
41303                 if(hname == 'down' || this.scope.isExpanded()){
41304                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41305                 }
41306                 return true;
41307             },
41308
41309             forceKeyDown: true
41310         });
41311         this.queryDelay = Math.max(this.queryDelay || 10,
41312                 this.mode == 'local' ? 10 : 250);
41313         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41314         if(this.typeAhead){
41315             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41316         }
41317         if(this.editable !== false){
41318             this.el.on("keyup", this.onKeyUp, this);
41319         }
41320         if(this.forceSelection){
41321             this.on('blur', this.doForce, this);
41322         }
41323     },
41324
41325     onDestroy : function(){
41326         if(this.view){
41327             this.view.setStore(null);
41328             this.view.el.removeAllListeners();
41329             this.view.el.remove();
41330             this.view.purgeListeners();
41331         }
41332         if(this.list){
41333             this.list.destroy();
41334         }
41335         if(this.store){
41336             this.store.un('beforeload', this.onBeforeLoad, this);
41337             this.store.un('load', this.onLoad, this);
41338             this.store.un('loadexception', this.onLoadException, this);
41339         }
41340         Roo.form.ComboBox.superclass.onDestroy.call(this);
41341     },
41342
41343     // private
41344     fireKey : function(e){
41345         if(e.isNavKeyPress() && !this.list.isVisible()){
41346             this.fireEvent("specialkey", this, e);
41347         }
41348     },
41349
41350     // private
41351     onResize: function(w, h){
41352         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41353         
41354         if(typeof w != 'number'){
41355             // we do not handle it!?!?
41356             return;
41357         }
41358         var tw = this.trigger.getWidth();
41359         tw += this.addicon ? this.addicon.getWidth() : 0;
41360         tw += this.editicon ? this.editicon.getWidth() : 0;
41361         var x = w - tw;
41362         this.el.setWidth( this.adjustWidth('input', x));
41363             
41364         this.trigger.setStyle('left', x+'px');
41365         
41366         if(this.list && this.listWidth === undefined){
41367             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41368             this.list.setWidth(lw);
41369             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41370         }
41371         
41372     
41373         
41374     },
41375
41376     /**
41377      * Allow or prevent the user from directly editing the field text.  If false is passed,
41378      * the user will only be able to select from the items defined in the dropdown list.  This method
41379      * is the runtime equivalent of setting the 'editable' config option at config time.
41380      * @param {Boolean} value True to allow the user to directly edit the field text
41381      */
41382     setEditable : function(value){
41383         if(value == this.editable){
41384             return;
41385         }
41386         this.editable = value;
41387         if(!value){
41388             this.el.dom.setAttribute('readOnly', true);
41389             this.el.on('mousedown', this.onTriggerClick,  this);
41390             this.el.addClass('x-combo-noedit');
41391         }else{
41392             this.el.dom.setAttribute('readOnly', false);
41393             this.el.un('mousedown', this.onTriggerClick,  this);
41394             this.el.removeClass('x-combo-noedit');
41395         }
41396     },
41397
41398     // private
41399     onBeforeLoad : function(){
41400         if(!this.hasFocus){
41401             return;
41402         }
41403         this.innerList.update(this.loadingText ?
41404                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41405         this.restrictHeight();
41406         this.selectedIndex = -1;
41407     },
41408
41409     // private
41410     onLoad : function(){
41411         if(!this.hasFocus){
41412             return;
41413         }
41414         if(this.store.getCount() > 0){
41415             this.expand();
41416             this.restrictHeight();
41417             if(this.lastQuery == this.allQuery){
41418                 if(this.editable){
41419                     this.el.dom.select();
41420                 }
41421                 if(!this.selectByValue(this.value, true)){
41422                     this.select(0, true);
41423                 }
41424             }else{
41425                 this.selectNext();
41426                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41427                     this.taTask.delay(this.typeAheadDelay);
41428                 }
41429             }
41430         }else{
41431             this.onEmptyResults();
41432         }
41433         //this.el.focus();
41434     },
41435     // private
41436     onLoadException : function()
41437     {
41438         this.collapse();
41439         Roo.log(this.store.reader.jsonData);
41440         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41441             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41442         }
41443         
41444         
41445     },
41446     // private
41447     onTypeAhead : function(){
41448         if(this.store.getCount() > 0){
41449             var r = this.store.getAt(0);
41450             var newValue = r.data[this.displayField];
41451             var len = newValue.length;
41452             var selStart = this.getRawValue().length;
41453             if(selStart != len){
41454                 this.setRawValue(newValue);
41455                 this.selectText(selStart, newValue.length);
41456             }
41457         }
41458     },
41459
41460     // private
41461     onSelect : function(record, index){
41462         if(this.fireEvent('beforeselect', this, record, index) !== false){
41463             this.setFromData(index > -1 ? record.data : false);
41464             this.collapse();
41465             this.fireEvent('select', this, record, index);
41466         }
41467     },
41468
41469     /**
41470      * Returns the currently selected field value or empty string if no value is set.
41471      * @return {String} value The selected value
41472      */
41473     getValue : function(){
41474         if(this.valueField){
41475             return typeof this.value != 'undefined' ? this.value : '';
41476         }
41477         return Roo.form.ComboBox.superclass.getValue.call(this);
41478     },
41479
41480     /**
41481      * Clears any text/value currently set in the field
41482      */
41483     clearValue : function(){
41484         if(this.hiddenField){
41485             this.hiddenField.value = '';
41486         }
41487         this.value = '';
41488         this.setRawValue('');
41489         this.lastSelectionText = '';
41490         
41491     },
41492
41493     /**
41494      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41495      * will be displayed in the field.  If the value does not match the data value of an existing item,
41496      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41497      * Otherwise the field will be blank (although the value will still be set).
41498      * @param {String} value The value to match
41499      */
41500     setValue : function(v){
41501         var text = v;
41502         if(this.valueField){
41503             var r = this.findRecord(this.valueField, v);
41504             if(r){
41505                 text = r.data[this.displayField];
41506             }else if(this.valueNotFoundText !== undefined){
41507                 text = this.valueNotFoundText;
41508             }
41509         }
41510         this.lastSelectionText = text;
41511         if(this.hiddenField){
41512             this.hiddenField.value = v;
41513         }
41514         Roo.form.ComboBox.superclass.setValue.call(this, text);
41515         this.value = v;
41516     },
41517     /**
41518      * @property {Object} the last set data for the element
41519      */
41520     
41521     lastData : false,
41522     /**
41523      * Sets the value of the field based on a object which is related to the record format for the store.
41524      * @param {Object} value the value to set as. or false on reset?
41525      */
41526     setFromData : function(o){
41527         var dv = ''; // display value
41528         var vv = ''; // value value..
41529         this.lastData = o;
41530         if (this.displayField) {
41531             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41532         } else {
41533             // this is an error condition!!!
41534             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41535         }
41536         
41537         if(this.valueField){
41538             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41539         }
41540         if(this.hiddenField){
41541             this.hiddenField.value = vv;
41542             
41543             this.lastSelectionText = dv;
41544             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41545             this.value = vv;
41546             return;
41547         }
41548         // no hidden field.. - we store the value in 'value', but still display
41549         // display field!!!!
41550         this.lastSelectionText = dv;
41551         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41552         this.value = vv;
41553         
41554         
41555     },
41556     // private
41557     reset : function(){
41558         // overridden so that last data is reset..
41559         this.setValue(this.resetValue);
41560         this.clearInvalid();
41561         this.lastData = false;
41562         if (this.view) {
41563             this.view.clearSelections();
41564         }
41565     },
41566     // private
41567     findRecord : function(prop, value){
41568         var record;
41569         if(this.store.getCount() > 0){
41570             this.store.each(function(r){
41571                 if(r.data[prop] == value){
41572                     record = r;
41573                     return false;
41574                 }
41575                 return true;
41576             });
41577         }
41578         return record;
41579     },
41580     
41581     getName: function()
41582     {
41583         // returns hidden if it's set..
41584         if (!this.rendered) {return ''};
41585         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41586         
41587     },
41588     // private
41589     onViewMove : function(e, t){
41590         this.inKeyMode = false;
41591     },
41592
41593     // private
41594     onViewOver : function(e, t){
41595         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41596             return;
41597         }
41598         var item = this.view.findItemFromChild(t);
41599         if(item){
41600             var index = this.view.indexOf(item);
41601             this.select(index, false);
41602         }
41603     },
41604
41605     // private
41606     onViewClick : function(doFocus)
41607     {
41608         var index = this.view.getSelectedIndexes()[0];
41609         var r = this.store.getAt(index);
41610         if(r){
41611             this.onSelect(r, index);
41612         }
41613         if(doFocus !== false && !this.blockFocus){
41614             this.el.focus();
41615         }
41616     },
41617
41618     // private
41619     restrictHeight : function(){
41620         this.innerList.dom.style.height = '';
41621         var inner = this.innerList.dom;
41622         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41623         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41624         this.list.beginUpdate();
41625         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41626         this.list.alignTo(this.el, this.listAlign);
41627         this.list.endUpdate();
41628     },
41629
41630     // private
41631     onEmptyResults : function(){
41632         this.collapse();
41633     },
41634
41635     /**
41636      * Returns true if the dropdown list is expanded, else false.
41637      */
41638     isExpanded : function(){
41639         return this.list.isVisible();
41640     },
41641
41642     /**
41643      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41644      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41645      * @param {String} value The data value of the item to select
41646      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41647      * selected item if it is not currently in view (defaults to true)
41648      * @return {Boolean} True if the value matched an item in the list, else false
41649      */
41650     selectByValue : function(v, scrollIntoView){
41651         if(v !== undefined && v !== null){
41652             var r = this.findRecord(this.valueField || this.displayField, v);
41653             if(r){
41654                 this.select(this.store.indexOf(r), scrollIntoView);
41655                 return true;
41656             }
41657         }
41658         return false;
41659     },
41660
41661     /**
41662      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41663      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41664      * @param {Number} index The zero-based index of the list item to select
41665      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41666      * selected item if it is not currently in view (defaults to true)
41667      */
41668     select : function(index, scrollIntoView){
41669         this.selectedIndex = index;
41670         this.view.select(index);
41671         if(scrollIntoView !== false){
41672             var el = this.view.getNode(index);
41673             if(el){
41674                 this.innerList.scrollChildIntoView(el, false);
41675             }
41676         }
41677     },
41678
41679     // private
41680     selectNext : function(){
41681         var ct = this.store.getCount();
41682         if(ct > 0){
41683             if(this.selectedIndex == -1){
41684                 this.select(0);
41685             }else if(this.selectedIndex < ct-1){
41686                 this.select(this.selectedIndex+1);
41687             }
41688         }
41689     },
41690
41691     // private
41692     selectPrev : function(){
41693         var ct = this.store.getCount();
41694         if(ct > 0){
41695             if(this.selectedIndex == -1){
41696                 this.select(0);
41697             }else if(this.selectedIndex != 0){
41698                 this.select(this.selectedIndex-1);
41699             }
41700         }
41701     },
41702
41703     // private
41704     onKeyUp : function(e){
41705         if(this.editable !== false && !e.isSpecialKey()){
41706             this.lastKey = e.getKey();
41707             this.dqTask.delay(this.queryDelay);
41708         }
41709     },
41710
41711     // private
41712     validateBlur : function(){
41713         return !this.list || !this.list.isVisible();   
41714     },
41715
41716     // private
41717     initQuery : function(){
41718         this.doQuery(this.getRawValue());
41719     },
41720
41721     // private
41722     doForce : function(){
41723         if(this.el.dom.value.length > 0){
41724             this.el.dom.value =
41725                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41726              
41727         }
41728     },
41729
41730     /**
41731      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41732      * query allowing the query action to be canceled if needed.
41733      * @param {String} query The SQL query to execute
41734      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41735      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41736      * saved in the current store (defaults to false)
41737      */
41738     doQuery : function(q, forceAll){
41739         if(q === undefined || q === null){
41740             q = '';
41741         }
41742         var qe = {
41743             query: q,
41744             forceAll: forceAll,
41745             combo: this,
41746             cancel:false
41747         };
41748         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41749             return false;
41750         }
41751         q = qe.query;
41752         forceAll = qe.forceAll;
41753         if(forceAll === true || (q.length >= this.minChars)){
41754             if(this.lastQuery != q || this.alwaysQuery){
41755                 this.lastQuery = q;
41756                 if(this.mode == 'local'){
41757                     this.selectedIndex = -1;
41758                     if(forceAll){
41759                         this.store.clearFilter();
41760                     }else{
41761                         this.store.filter(this.displayField, q);
41762                     }
41763                     this.onLoad();
41764                 }else{
41765                     this.store.baseParams[this.queryParam] = q;
41766                     this.store.load({
41767                         params: this.getParams(q)
41768                     });
41769                     this.expand();
41770                 }
41771             }else{
41772                 this.selectedIndex = -1;
41773                 this.onLoad();   
41774             }
41775         }
41776     },
41777
41778     // private
41779     getParams : function(q){
41780         var p = {};
41781         //p[this.queryParam] = q;
41782         if(this.pageSize){
41783             p.start = 0;
41784             p.limit = this.pageSize;
41785         }
41786         return p;
41787     },
41788
41789     /**
41790      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41791      */
41792     collapse : function(){
41793         if(!this.isExpanded()){
41794             return;
41795         }
41796         this.list.hide();
41797         Roo.get(document).un('mousedown', this.collapseIf, this);
41798         Roo.get(document).un('mousewheel', this.collapseIf, this);
41799         if (!this.editable) {
41800             Roo.get(document).un('keydown', this.listKeyPress, this);
41801         }
41802         this.fireEvent('collapse', this);
41803     },
41804
41805     // private
41806     collapseIf : function(e){
41807         if(!e.within(this.wrap) && !e.within(this.list)){
41808             this.collapse();
41809         }
41810     },
41811
41812     /**
41813      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41814      */
41815     expand : function(){
41816         if(this.isExpanded() || !this.hasFocus){
41817             return;
41818         }
41819         this.list.alignTo(this.el, this.listAlign);
41820         this.list.show();
41821         Roo.get(document).on('mousedown', this.collapseIf, this);
41822         Roo.get(document).on('mousewheel', this.collapseIf, this);
41823         if (!this.editable) {
41824             Roo.get(document).on('keydown', this.listKeyPress, this);
41825         }
41826         
41827         this.fireEvent('expand', this);
41828     },
41829
41830     // private
41831     // Implements the default empty TriggerField.onTriggerClick function
41832     onTriggerClick : function(){
41833         if(this.disabled){
41834             return;
41835         }
41836         if(this.isExpanded()){
41837             this.collapse();
41838             if (!this.blockFocus) {
41839                 this.el.focus();
41840             }
41841             
41842         }else {
41843             this.hasFocus = true;
41844             if(this.triggerAction == 'all') {
41845                 this.doQuery(this.allQuery, true);
41846             } else {
41847                 this.doQuery(this.getRawValue());
41848             }
41849             if (!this.blockFocus) {
41850                 this.el.focus();
41851             }
41852         }
41853     },
41854     listKeyPress : function(e)
41855     {
41856         //Roo.log('listkeypress');
41857         // scroll to first matching element based on key pres..
41858         if (e.isSpecialKey()) {
41859             return false;
41860         }
41861         var k = String.fromCharCode(e.getKey()).toUpperCase();
41862         //Roo.log(k);
41863         var match  = false;
41864         var csel = this.view.getSelectedNodes();
41865         var cselitem = false;
41866         if (csel.length) {
41867             var ix = this.view.indexOf(csel[0]);
41868             cselitem  = this.store.getAt(ix);
41869             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41870                 cselitem = false;
41871             }
41872             
41873         }
41874         
41875         this.store.each(function(v) { 
41876             if (cselitem) {
41877                 // start at existing selection.
41878                 if (cselitem.id == v.id) {
41879                     cselitem = false;
41880                 }
41881                 return;
41882             }
41883                 
41884             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41885                 match = this.store.indexOf(v);
41886                 return false;
41887             }
41888         }, this);
41889         
41890         if (match === false) {
41891             return true; // no more action?
41892         }
41893         // scroll to?
41894         this.view.select(match);
41895         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41896         sn.scrollIntoView(sn.dom.parentNode, false);
41897     }
41898
41899     /** 
41900     * @cfg {Boolean} grow 
41901     * @hide 
41902     */
41903     /** 
41904     * @cfg {Number} growMin 
41905     * @hide 
41906     */
41907     /** 
41908     * @cfg {Number} growMax 
41909     * @hide 
41910     */
41911     /**
41912      * @hide
41913      * @method autoSize
41914      */
41915 });/*
41916  * Copyright(c) 2010-2012, Roo J Solutions Limited
41917  *
41918  * Licence LGPL
41919  *
41920  */
41921
41922 /**
41923  * @class Roo.form.ComboBoxArray
41924  * @extends Roo.form.TextField
41925  * A facebook style adder... for lists of email / people / countries  etc...
41926  * pick multiple items from a combo box, and shows each one.
41927  *
41928  *  Fred [x]  Brian [x]  [Pick another |v]
41929  *
41930  *
41931  *  For this to work: it needs various extra information
41932  *    - normal combo problay has
41933  *      name, hiddenName
41934  *    + displayField, valueField
41935  *
41936  *    For our purpose...
41937  *
41938  *
41939  *   If we change from 'extends' to wrapping...
41940  *   
41941  *  
41942  *
41943  
41944  
41945  * @constructor
41946  * Create a new ComboBoxArray.
41947  * @param {Object} config Configuration options
41948  */
41949  
41950
41951 Roo.form.ComboBoxArray = function(config)
41952 {
41953     this.addEvents({
41954         /**
41955          * @event beforeremove
41956          * Fires before remove the value from the list
41957              * @param {Roo.form.ComboBoxArray} _self This combo box array
41958              * @param {Roo.form.ComboBoxArray.Item} item removed item
41959              */
41960         'beforeremove' : true,
41961         /**
41962          * @event remove
41963          * Fires when remove the value from the list
41964              * @param {Roo.form.ComboBoxArray} _self This combo box array
41965              * @param {Roo.form.ComboBoxArray.Item} item removed item
41966              */
41967         'remove' : true
41968         
41969         
41970     });
41971     
41972     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41973     
41974     this.items = new Roo.util.MixedCollection(false);
41975     
41976     // construct the child combo...
41977     
41978     
41979     
41980     
41981    
41982     
41983 }
41984
41985  
41986 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41987
41988     /**
41989      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41990      */
41991     
41992     lastData : false,
41993     
41994     // behavies liek a hiddne field
41995     inputType:      'hidden',
41996     /**
41997      * @cfg {Number} width The width of the box that displays the selected element
41998      */ 
41999     width:          300,
42000
42001     
42002     
42003     /**
42004      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42005      */
42006     name : false,
42007     /**
42008      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42009      */
42010     hiddenName : false,
42011     
42012     
42013     // private the array of items that are displayed..
42014     items  : false,
42015     // private - the hidden field el.
42016     hiddenEl : false,
42017     // private - the filed el..
42018     el : false,
42019     
42020     //validateValue : function() { return true; }, // all values are ok!
42021     //onAddClick: function() { },
42022     
42023     onRender : function(ct, position) 
42024     {
42025         
42026         // create the standard hidden element
42027         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42028         
42029         
42030         // give fake names to child combo;
42031         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42032         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42033         
42034         this.combo = Roo.factory(this.combo, Roo.form);
42035         this.combo.onRender(ct, position);
42036         if (typeof(this.combo.width) != 'undefined') {
42037             this.combo.onResize(this.combo.width,0);
42038         }
42039         
42040         this.combo.initEvents();
42041         
42042         // assigned so form know we need to do this..
42043         this.store          = this.combo.store;
42044         this.valueField     = this.combo.valueField;
42045         this.displayField   = this.combo.displayField ;
42046         
42047         
42048         this.combo.wrap.addClass('x-cbarray-grp');
42049         
42050         var cbwrap = this.combo.wrap.createChild(
42051             {tag: 'div', cls: 'x-cbarray-cb'},
42052             this.combo.el.dom
42053         );
42054         
42055              
42056         this.hiddenEl = this.combo.wrap.createChild({
42057             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42058         });
42059         this.el = this.combo.wrap.createChild({
42060             tag: 'input',  type:'hidden' , name: this.name, value : ''
42061         });
42062          //   this.el.dom.removeAttribute("name");
42063         
42064         
42065         this.outerWrap = this.combo.wrap;
42066         this.wrap = cbwrap;
42067         
42068         this.outerWrap.setWidth(this.width);
42069         this.outerWrap.dom.removeChild(this.el.dom);
42070         
42071         this.wrap.dom.appendChild(this.el.dom);
42072         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42073         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42074         
42075         this.combo.trigger.setStyle('position','relative');
42076         this.combo.trigger.setStyle('left', '0px');
42077         this.combo.trigger.setStyle('top', '2px');
42078         
42079         this.combo.el.setStyle('vertical-align', 'text-bottom');
42080         
42081         //this.trigger.setStyle('vertical-align', 'top');
42082         
42083         // this should use the code from combo really... on('add' ....)
42084         if (this.adder) {
42085             
42086         
42087             this.adder = this.outerWrap.createChild(
42088                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42089             var _t = this;
42090             this.adder.on('click', function(e) {
42091                 _t.fireEvent('adderclick', this, e);
42092             }, _t);
42093         }
42094         //var _t = this;
42095         //this.adder.on('click', this.onAddClick, _t);
42096         
42097         
42098         this.combo.on('select', function(cb, rec, ix) {
42099             this.addItem(rec.data);
42100             
42101             cb.setValue('');
42102             cb.el.dom.value = '';
42103             //cb.lastData = rec.data;
42104             // add to list
42105             
42106         }, this);
42107         
42108         
42109     },
42110     
42111     
42112     getName: function()
42113     {
42114         // returns hidden if it's set..
42115         if (!this.rendered) {return ''};
42116         return  this.hiddenName ? this.hiddenName : this.name;
42117         
42118     },
42119     
42120     
42121     onResize: function(w, h){
42122         
42123         return;
42124         // not sure if this is needed..
42125         //this.combo.onResize(w,h);
42126         
42127         if(typeof w != 'number'){
42128             // we do not handle it!?!?
42129             return;
42130         }
42131         var tw = this.combo.trigger.getWidth();
42132         tw += this.addicon ? this.addicon.getWidth() : 0;
42133         tw += this.editicon ? this.editicon.getWidth() : 0;
42134         var x = w - tw;
42135         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42136             
42137         this.combo.trigger.setStyle('left', '0px');
42138         
42139         if(this.list && this.listWidth === undefined){
42140             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42141             this.list.setWidth(lw);
42142             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42143         }
42144         
42145     
42146         
42147     },
42148     
42149     addItem: function(rec)
42150     {
42151         var valueField = this.combo.valueField;
42152         var displayField = this.combo.displayField;
42153         if (this.items.indexOfKey(rec[valueField]) > -1) {
42154             //console.log("GOT " + rec.data.id);
42155             return;
42156         }
42157         
42158         var x = new Roo.form.ComboBoxArray.Item({
42159             //id : rec[this.idField],
42160             data : rec,
42161             displayField : displayField ,
42162             tipField : displayField ,
42163             cb : this
42164         });
42165         // use the 
42166         this.items.add(rec[valueField],x);
42167         // add it before the element..
42168         this.updateHiddenEl();
42169         x.render(this.outerWrap, this.wrap.dom);
42170         // add the image handler..
42171     },
42172     
42173     updateHiddenEl : function()
42174     {
42175         this.validate();
42176         if (!this.hiddenEl) {
42177             return;
42178         }
42179         var ar = [];
42180         var idField = this.combo.valueField;
42181         
42182         this.items.each(function(f) {
42183             ar.push(f.data[idField]);
42184            
42185         });
42186         this.hiddenEl.dom.value = ar.join(',');
42187         this.validate();
42188     },
42189     
42190     reset : function()
42191     {
42192         this.items.clear();
42193         
42194         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42195            el.remove();
42196         });
42197         
42198         this.el.dom.value = '';
42199         if (this.hiddenEl) {
42200             this.hiddenEl.dom.value = '';
42201         }
42202         
42203     },
42204     getValue: function()
42205     {
42206         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42207     },
42208     setValue: function(v) // not a valid action - must use addItems..
42209     {
42210          
42211         this.reset();
42212         
42213         
42214         
42215         if (this.store.isLocal && (typeof(v) == 'string')) {
42216             // then we can use the store to find the values..
42217             // comma seperated at present.. this needs to allow JSON based encoding..
42218             this.hiddenEl.value  = v;
42219             var v_ar = [];
42220             Roo.each(v.split(','), function(k) {
42221                 Roo.log("CHECK " + this.valueField + ',' + k);
42222                 var li = this.store.query(this.valueField, k);
42223                 if (!li.length) {
42224                     return;
42225                 }
42226                 var add = {};
42227                 add[this.valueField] = k;
42228                 add[this.displayField] = li.item(0).data[this.displayField];
42229                 
42230                 this.addItem(add);
42231             }, this) 
42232              
42233         }
42234         if (typeof(v) == 'object' ) {
42235             // then let's assume it's an array of objects..
42236             Roo.each(v, function(l) {
42237                 this.addItem(l);
42238             }, this);
42239              
42240         }
42241         
42242         
42243     },
42244     setFromData: function(v)
42245     {
42246         // this recieves an object, if setValues is called.
42247         this.reset();
42248         this.el.dom.value = v[this.displayField];
42249         this.hiddenEl.dom.value = v[this.valueField];
42250         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42251             return;
42252         }
42253         var kv = v[this.valueField];
42254         var dv = v[this.displayField];
42255         kv = typeof(kv) != 'string' ? '' : kv;
42256         dv = typeof(dv) != 'string' ? '' : dv;
42257         
42258         
42259         var keys = kv.split(',');
42260         var display = dv.split(',');
42261         for (var i = 0 ; i < keys.length; i++) {
42262             
42263             add = {};
42264             add[this.valueField] = keys[i];
42265             add[this.displayField] = display[i];
42266             this.addItem(add);
42267         }
42268       
42269         
42270     },
42271     
42272     /**
42273      * Validates the combox array value
42274      * @return {Boolean} True if the value is valid, else false
42275      */
42276     validate : function(){
42277         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42278             this.clearInvalid();
42279             return true;
42280         }
42281         return false;
42282     },
42283     
42284     validateValue : function(value){
42285         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42286         
42287     },
42288     
42289     /*@
42290      * overide
42291      * 
42292      */
42293     isDirty : function() {
42294         if(this.disabled) {
42295             return false;
42296         }
42297         
42298         try {
42299             var d = Roo.decode(String(this.originalValue));
42300         } catch (e) {
42301             return String(this.getValue()) !== String(this.originalValue);
42302         }
42303         
42304         var originalValue = [];
42305         
42306         for (var i = 0; i < d.length; i++){
42307             originalValue.push(d[i][this.valueField]);
42308         }
42309         
42310         return String(this.getValue()) !== String(originalValue.join(','));
42311         
42312     }
42313     
42314 });
42315
42316
42317
42318 /**
42319  * @class Roo.form.ComboBoxArray.Item
42320  * @extends Roo.BoxComponent
42321  * A selected item in the list
42322  *  Fred [x]  Brian [x]  [Pick another |v]
42323  * 
42324  * @constructor
42325  * Create a new item.
42326  * @param {Object} config Configuration options
42327  */
42328  
42329 Roo.form.ComboBoxArray.Item = function(config) {
42330     config.id = Roo.id();
42331     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42332 }
42333
42334 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42335     data : {},
42336     cb: false,
42337     displayField : false,
42338     tipField : false,
42339     
42340     
42341     defaultAutoCreate : {
42342         tag: 'div',
42343         cls: 'x-cbarray-item',
42344         cn : [ 
42345             { tag: 'div' },
42346             {
42347                 tag: 'img',
42348                 width:16,
42349                 height : 16,
42350                 src : Roo.BLANK_IMAGE_URL ,
42351                 align: 'center'
42352             }
42353         ]
42354         
42355     },
42356     
42357  
42358     onRender : function(ct, position)
42359     {
42360         Roo.form.Field.superclass.onRender.call(this, ct, position);
42361         
42362         if(!this.el){
42363             var cfg = this.getAutoCreate();
42364             this.el = ct.createChild(cfg, position);
42365         }
42366         
42367         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42368         
42369         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42370             this.cb.renderer(this.data) :
42371             String.format('{0}',this.data[this.displayField]);
42372         
42373             
42374         this.el.child('div').dom.setAttribute('qtip',
42375                         String.format('{0}',this.data[this.tipField])
42376         );
42377         
42378         this.el.child('img').on('click', this.remove, this);
42379         
42380     },
42381    
42382     remove : function()
42383     {
42384         if(this.cb.disabled){
42385             return;
42386         }
42387         
42388         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42389             this.cb.items.remove(this);
42390             this.el.child('img').un('click', this.remove, this);
42391             this.el.remove();
42392             this.cb.updateHiddenEl();
42393
42394             this.cb.fireEvent('remove', this.cb, this);
42395         }
42396         
42397     }
42398 });/*
42399  * Based on:
42400  * Ext JS Library 1.1.1
42401  * Copyright(c) 2006-2007, Ext JS, LLC.
42402  *
42403  * Originally Released Under LGPL - original licence link has changed is not relivant.
42404  *
42405  * Fork - LGPL
42406  * <script type="text/javascript">
42407  */
42408 /**
42409  * @class Roo.form.Checkbox
42410  * @extends Roo.form.Field
42411  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42412  * @constructor
42413  * Creates a new Checkbox
42414  * @param {Object} config Configuration options
42415  */
42416 Roo.form.Checkbox = function(config){
42417     Roo.form.Checkbox.superclass.constructor.call(this, config);
42418     this.addEvents({
42419         /**
42420          * @event check
42421          * Fires when the checkbox is checked or unchecked.
42422              * @param {Roo.form.Checkbox} this This checkbox
42423              * @param {Boolean} checked The new checked value
42424              */
42425         check : true
42426     });
42427 };
42428
42429 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42430     /**
42431      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42432      */
42433     focusClass : undefined,
42434     /**
42435      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42436      */
42437     fieldClass: "x-form-field",
42438     /**
42439      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42440      */
42441     checked: false,
42442     /**
42443      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42444      * {tag: "input", type: "checkbox", autocomplete: "off"})
42445      */
42446     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42447     /**
42448      * @cfg {String} boxLabel The text that appears beside the checkbox
42449      */
42450     boxLabel : "",
42451     /**
42452      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42453      */  
42454     inputValue : '1',
42455     /**
42456      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42457      */
42458      valueOff: '0', // value when not checked..
42459
42460     actionMode : 'viewEl', 
42461     //
42462     // private
42463     itemCls : 'x-menu-check-item x-form-item',
42464     groupClass : 'x-menu-group-item',
42465     inputType : 'hidden',
42466     
42467     
42468     inSetChecked: false, // check that we are not calling self...
42469     
42470     inputElement: false, // real input element?
42471     basedOn: false, // ????
42472     
42473     isFormField: true, // not sure where this is needed!!!!
42474
42475     onResize : function(){
42476         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42477         if(!this.boxLabel){
42478             this.el.alignTo(this.wrap, 'c-c');
42479         }
42480     },
42481
42482     initEvents : function(){
42483         Roo.form.Checkbox.superclass.initEvents.call(this);
42484         this.el.on("click", this.onClick,  this);
42485         this.el.on("change", this.onClick,  this);
42486     },
42487
42488
42489     getResizeEl : function(){
42490         return this.wrap;
42491     },
42492
42493     getPositionEl : function(){
42494         return this.wrap;
42495     },
42496
42497     // private
42498     onRender : function(ct, position){
42499         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42500         /*
42501         if(this.inputValue !== undefined){
42502             this.el.dom.value = this.inputValue;
42503         }
42504         */
42505         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42506         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42507         var viewEl = this.wrap.createChild({ 
42508             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42509         this.viewEl = viewEl;   
42510         this.wrap.on('click', this.onClick,  this); 
42511         
42512         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42513         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42514         
42515         
42516         
42517         if(this.boxLabel){
42518             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42519         //    viewEl.on('click', this.onClick,  this); 
42520         }
42521         //if(this.checked){
42522             this.setChecked(this.checked);
42523         //}else{
42524             //this.checked = this.el.dom;
42525         //}
42526
42527     },
42528
42529     // private
42530     initValue : Roo.emptyFn,
42531
42532     /**
42533      * Returns the checked state of the checkbox.
42534      * @return {Boolean} True if checked, else false
42535      */
42536     getValue : function(){
42537         if(this.el){
42538             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42539         }
42540         return this.valueOff;
42541         
42542     },
42543
42544         // private
42545     onClick : function(){ 
42546         if (this.disabled) {
42547             return;
42548         }
42549         this.setChecked(!this.checked);
42550
42551         //if(this.el.dom.checked != this.checked){
42552         //    this.setValue(this.el.dom.checked);
42553        // }
42554     },
42555
42556     /**
42557      * Sets the checked state of the checkbox.
42558      * On is always based on a string comparison between inputValue and the param.
42559      * @param {Boolean/String} value - the value to set 
42560      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42561      */
42562     setValue : function(v,suppressEvent){
42563         
42564         
42565         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42566         //if(this.el && this.el.dom){
42567         //    this.el.dom.checked = this.checked;
42568         //    this.el.dom.defaultChecked = this.checked;
42569         //}
42570         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42571         //this.fireEvent("check", this, this.checked);
42572     },
42573     // private..
42574     setChecked : function(state,suppressEvent)
42575     {
42576         if (this.inSetChecked) {
42577             this.checked = state;
42578             return;
42579         }
42580         
42581     
42582         if(this.wrap){
42583             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42584         }
42585         this.checked = state;
42586         if(suppressEvent !== true){
42587             this.fireEvent('check', this, state);
42588         }
42589         this.inSetChecked = true;
42590         this.el.dom.value = state ? this.inputValue : this.valueOff;
42591         this.inSetChecked = false;
42592         
42593     },
42594     // handle setting of hidden value by some other method!!?!?
42595     setFromHidden: function()
42596     {
42597         if(!this.el){
42598             return;
42599         }
42600         //console.log("SET FROM HIDDEN");
42601         //alert('setFrom hidden');
42602         this.setValue(this.el.dom.value);
42603     },
42604     
42605     onDestroy : function()
42606     {
42607         if(this.viewEl){
42608             Roo.get(this.viewEl).remove();
42609         }
42610          
42611         Roo.form.Checkbox.superclass.onDestroy.call(this);
42612     },
42613     
42614     setBoxLabel : function(str)
42615     {
42616         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42617     }
42618
42619 });/*
42620  * Based on:
42621  * Ext JS Library 1.1.1
42622  * Copyright(c) 2006-2007, Ext JS, LLC.
42623  *
42624  * Originally Released Under LGPL - original licence link has changed is not relivant.
42625  *
42626  * Fork - LGPL
42627  * <script type="text/javascript">
42628  */
42629  
42630 /**
42631  * @class Roo.form.Radio
42632  * @extends Roo.form.Checkbox
42633  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42634  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42635  * @constructor
42636  * Creates a new Radio
42637  * @param {Object} config Configuration options
42638  */
42639 Roo.form.Radio = function(){
42640     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42641 };
42642 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42643     inputType: 'radio',
42644
42645     /**
42646      * If this radio is part of a group, it will return the selected value
42647      * @return {String}
42648      */
42649     getGroupValue : function(){
42650         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42651     },
42652     
42653     
42654     onRender : function(ct, position){
42655         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42656         
42657         if(this.inputValue !== undefined){
42658             this.el.dom.value = this.inputValue;
42659         }
42660          
42661         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42662         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42663         //var viewEl = this.wrap.createChild({ 
42664         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42665         //this.viewEl = viewEl;   
42666         //this.wrap.on('click', this.onClick,  this); 
42667         
42668         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42669         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42670         
42671         
42672         
42673         if(this.boxLabel){
42674             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42675         //    viewEl.on('click', this.onClick,  this); 
42676         }
42677          if(this.checked){
42678             this.el.dom.checked =   'checked' ;
42679         }
42680          
42681     } 
42682     
42683     
42684 });//<script type="text/javascript">
42685
42686 /*
42687  * Based  Ext JS Library 1.1.1
42688  * Copyright(c) 2006-2007, Ext JS, LLC.
42689  * LGPL
42690  *
42691  */
42692  
42693 /**
42694  * @class Roo.HtmlEditorCore
42695  * @extends Roo.Component
42696  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42697  *
42698  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42699  */
42700
42701 Roo.HtmlEditorCore = function(config){
42702     
42703     
42704     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42705     
42706     
42707     this.addEvents({
42708         /**
42709          * @event initialize
42710          * Fires when the editor is fully initialized (including the iframe)
42711          * @param {Roo.HtmlEditorCore} this
42712          */
42713         initialize: true,
42714         /**
42715          * @event activate
42716          * Fires when the editor is first receives the focus. Any insertion must wait
42717          * until after this event.
42718          * @param {Roo.HtmlEditorCore} this
42719          */
42720         activate: true,
42721          /**
42722          * @event beforesync
42723          * Fires before the textarea is updated with content from the editor iframe. Return false
42724          * to cancel the sync.
42725          * @param {Roo.HtmlEditorCore} this
42726          * @param {String} html
42727          */
42728         beforesync: true,
42729          /**
42730          * @event beforepush
42731          * Fires before the iframe editor is updated with content from the textarea. Return false
42732          * to cancel the push.
42733          * @param {Roo.HtmlEditorCore} this
42734          * @param {String} html
42735          */
42736         beforepush: true,
42737          /**
42738          * @event sync
42739          * Fires when the textarea is updated with content from the editor iframe.
42740          * @param {Roo.HtmlEditorCore} this
42741          * @param {String} html
42742          */
42743         sync: true,
42744          /**
42745          * @event push
42746          * Fires when the iframe editor is updated with content from the textarea.
42747          * @param {Roo.HtmlEditorCore} this
42748          * @param {String} html
42749          */
42750         push: true,
42751         
42752         /**
42753          * @event editorevent
42754          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42755          * @param {Roo.HtmlEditorCore} this
42756          */
42757         editorevent: true
42758         
42759     });
42760     
42761     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42762     
42763     // defaults : white / black...
42764     this.applyBlacklists();
42765     
42766     
42767     
42768 };
42769
42770
42771 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42772
42773
42774      /**
42775      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42776      */
42777     
42778     owner : false,
42779     
42780      /**
42781      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42782      *                        Roo.resizable.
42783      */
42784     resizable : false,
42785      /**
42786      * @cfg {Number} height (in pixels)
42787      */   
42788     height: 300,
42789    /**
42790      * @cfg {Number} width (in pixels)
42791      */   
42792     width: 500,
42793     
42794     /**
42795      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42796      * 
42797      */
42798     stylesheets: false,
42799     
42800     // id of frame..
42801     frameId: false,
42802     
42803     // private properties
42804     validationEvent : false,
42805     deferHeight: true,
42806     initialized : false,
42807     activated : false,
42808     sourceEditMode : false,
42809     onFocus : Roo.emptyFn,
42810     iframePad:3,
42811     hideMode:'offsets',
42812     
42813     clearUp: true,
42814     
42815     // blacklist + whitelisted elements..
42816     black: false,
42817     white: false,
42818      
42819     
42820
42821     /**
42822      * Protected method that will not generally be called directly. It
42823      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42824      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42825      */
42826     getDocMarkup : function(){
42827         // body styles..
42828         var st = '';
42829         
42830         // inherit styels from page...?? 
42831         if (this.stylesheets === false) {
42832             
42833             Roo.get(document.head).select('style').each(function(node) {
42834                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42835             });
42836             
42837             Roo.get(document.head).select('link').each(function(node) { 
42838                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42839             });
42840             
42841         } else if (!this.stylesheets.length) {
42842                 // simple..
42843                 st = '<style type="text/css">' +
42844                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42845                    '</style>';
42846         } else { 
42847             st = '<style type="text/css">' +
42848                     this.stylesheets +
42849                 '</style>';
42850         }
42851         
42852         st +=  '<style type="text/css">' +
42853             'IMG { cursor: pointer } ' +
42854         '</style>';
42855
42856         var cls = 'roo-htmleditor-body';
42857         
42858         if(this.bodyCls.length){
42859             cls += ' ' + this.bodyCls;
42860         }
42861         
42862         return '<html><head>' + st  +
42863             //<style type="text/css">' +
42864             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42865             //'</style>' +
42866             ' </head><body class="' +  cls + '"></body></html>';
42867     },
42868
42869     // private
42870     onRender : function(ct, position)
42871     {
42872         var _t = this;
42873         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42874         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42875         
42876         
42877         this.el.dom.style.border = '0 none';
42878         this.el.dom.setAttribute('tabIndex', -1);
42879         this.el.addClass('x-hidden hide');
42880         
42881         
42882         
42883         if(Roo.isIE){ // fix IE 1px bogus margin
42884             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42885         }
42886        
42887         
42888         this.frameId = Roo.id();
42889         
42890          
42891         
42892         var iframe = this.owner.wrap.createChild({
42893             tag: 'iframe',
42894             cls: 'form-control', // bootstrap..
42895             id: this.frameId,
42896             name: this.frameId,
42897             frameBorder : 'no',
42898             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42899         }, this.el
42900         );
42901         
42902         
42903         this.iframe = iframe.dom;
42904
42905          this.assignDocWin();
42906         
42907         this.doc.designMode = 'on';
42908        
42909         this.doc.open();
42910         this.doc.write(this.getDocMarkup());
42911         this.doc.close();
42912
42913         
42914         var task = { // must defer to wait for browser to be ready
42915             run : function(){
42916                 //console.log("run task?" + this.doc.readyState);
42917                 this.assignDocWin();
42918                 if(this.doc.body || this.doc.readyState == 'complete'){
42919                     try {
42920                         this.doc.designMode="on";
42921                     } catch (e) {
42922                         return;
42923                     }
42924                     Roo.TaskMgr.stop(task);
42925                     this.initEditor.defer(10, this);
42926                 }
42927             },
42928             interval : 10,
42929             duration: 10000,
42930             scope: this
42931         };
42932         Roo.TaskMgr.start(task);
42933
42934     },
42935
42936     // private
42937     onResize : function(w, h)
42938     {
42939          Roo.log('resize: ' +w + ',' + h );
42940         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42941         if(!this.iframe){
42942             return;
42943         }
42944         if(typeof w == 'number'){
42945             
42946             this.iframe.style.width = w + 'px';
42947         }
42948         if(typeof h == 'number'){
42949             
42950             this.iframe.style.height = h + 'px';
42951             if(this.doc){
42952                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42953             }
42954         }
42955         
42956     },
42957
42958     /**
42959      * Toggles the editor between standard and source edit mode.
42960      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42961      */
42962     toggleSourceEdit : function(sourceEditMode){
42963         
42964         this.sourceEditMode = sourceEditMode === true;
42965         
42966         if(this.sourceEditMode){
42967  
42968             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42969             
42970         }else{
42971             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42972             //this.iframe.className = '';
42973             this.deferFocus();
42974         }
42975         //this.setSize(this.owner.wrap.getSize());
42976         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42977     },
42978
42979     
42980   
42981
42982     /**
42983      * Protected method that will not generally be called directly. If you need/want
42984      * custom HTML cleanup, this is the method you should override.
42985      * @param {String} html The HTML to be cleaned
42986      * return {String} The cleaned HTML
42987      */
42988     cleanHtml : function(html){
42989         html = String(html);
42990         if(html.length > 5){
42991             if(Roo.isSafari){ // strip safari nonsense
42992                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42993             }
42994         }
42995         if(html == '&nbsp;'){
42996             html = '';
42997         }
42998         return html;
42999     },
43000
43001     /**
43002      * HTML Editor -> Textarea
43003      * Protected method that will not generally be called directly. Syncs the contents
43004      * of the editor iframe with the textarea.
43005      */
43006     syncValue : function(){
43007         if(this.initialized){
43008             var bd = (this.doc.body || this.doc.documentElement);
43009             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43010             var html = bd.innerHTML;
43011             if(Roo.isSafari){
43012                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43013                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43014                 if(m && m[1]){
43015                     html = '<div style="'+m[0]+'">' + html + '</div>';
43016                 }
43017             }
43018             html = this.cleanHtml(html);
43019             // fix up the special chars.. normaly like back quotes in word...
43020             // however we do not want to do this with chinese..
43021             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43022                 var cc = b.charCodeAt();
43023                 if (
43024                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43025                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43026                     (cc >= 0xf900 && cc < 0xfb00 )
43027                 ) {
43028                         return b;
43029                 }
43030                 return "&#"+cc+";" 
43031             });
43032             if(this.owner.fireEvent('beforesync', this, html) !== false){
43033                 this.el.dom.value = html;
43034                 this.owner.fireEvent('sync', this, html);
43035             }
43036         }
43037     },
43038
43039     /**
43040      * Protected method that will not generally be called directly. Pushes the value of the textarea
43041      * into the iframe editor.
43042      */
43043     pushValue : function(){
43044         if(this.initialized){
43045             var v = this.el.dom.value.trim();
43046             
43047 //            if(v.length < 1){
43048 //                v = '&#160;';
43049 //            }
43050             
43051             if(this.owner.fireEvent('beforepush', this, v) !== false){
43052                 var d = (this.doc.body || this.doc.documentElement);
43053                 d.innerHTML = v;
43054                 this.cleanUpPaste();
43055                 this.el.dom.value = d.innerHTML;
43056                 this.owner.fireEvent('push', this, v);
43057             }
43058         }
43059     },
43060
43061     // private
43062     deferFocus : function(){
43063         this.focus.defer(10, this);
43064     },
43065
43066     // doc'ed in Field
43067     focus : function(){
43068         if(this.win && !this.sourceEditMode){
43069             this.win.focus();
43070         }else{
43071             this.el.focus();
43072         }
43073     },
43074     
43075     assignDocWin: function()
43076     {
43077         var iframe = this.iframe;
43078         
43079          if(Roo.isIE){
43080             this.doc = iframe.contentWindow.document;
43081             this.win = iframe.contentWindow;
43082         } else {
43083 //            if (!Roo.get(this.frameId)) {
43084 //                return;
43085 //            }
43086 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43087 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43088             
43089             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43090                 return;
43091             }
43092             
43093             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43094             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43095         }
43096     },
43097     
43098     // private
43099     initEditor : function(){
43100         //console.log("INIT EDITOR");
43101         this.assignDocWin();
43102         
43103         
43104         
43105         this.doc.designMode="on";
43106         this.doc.open();
43107         this.doc.write(this.getDocMarkup());
43108         this.doc.close();
43109         
43110         var dbody = (this.doc.body || this.doc.documentElement);
43111         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43112         // this copies styles from the containing element into thsi one..
43113         // not sure why we need all of this..
43114         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43115         
43116         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43117         //ss['background-attachment'] = 'fixed'; // w3c
43118         dbody.bgProperties = 'fixed'; // ie
43119         //Roo.DomHelper.applyStyles(dbody, ss);
43120         Roo.EventManager.on(this.doc, {
43121             //'mousedown': this.onEditorEvent,
43122             'mouseup': this.onEditorEvent,
43123             'dblclick': this.onEditorEvent,
43124             'click': this.onEditorEvent,
43125             'keyup': this.onEditorEvent,
43126             buffer:100,
43127             scope: this
43128         });
43129         if(Roo.isGecko){
43130             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43131         }
43132         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43133             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43134         }
43135         this.initialized = true;
43136
43137         this.owner.fireEvent('initialize', this);
43138         this.pushValue();
43139     },
43140
43141     // private
43142     onDestroy : function(){
43143         
43144         
43145         
43146         if(this.rendered){
43147             
43148             //for (var i =0; i < this.toolbars.length;i++) {
43149             //    // fixme - ask toolbars for heights?
43150             //    this.toolbars[i].onDestroy();
43151            // }
43152             
43153             //this.wrap.dom.innerHTML = '';
43154             //this.wrap.remove();
43155         }
43156     },
43157
43158     // private
43159     onFirstFocus : function(){
43160         
43161         this.assignDocWin();
43162         
43163         
43164         this.activated = true;
43165          
43166     
43167         if(Roo.isGecko){ // prevent silly gecko errors
43168             this.win.focus();
43169             var s = this.win.getSelection();
43170             if(!s.focusNode || s.focusNode.nodeType != 3){
43171                 var r = s.getRangeAt(0);
43172                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43173                 r.collapse(true);
43174                 this.deferFocus();
43175             }
43176             try{
43177                 this.execCmd('useCSS', true);
43178                 this.execCmd('styleWithCSS', false);
43179             }catch(e){}
43180         }
43181         this.owner.fireEvent('activate', this);
43182     },
43183
43184     // private
43185     adjustFont: function(btn){
43186         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43187         //if(Roo.isSafari){ // safari
43188         //    adjust *= 2;
43189        // }
43190         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43191         if(Roo.isSafari){ // safari
43192             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43193             v =  (v < 10) ? 10 : v;
43194             v =  (v > 48) ? 48 : v;
43195             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43196             
43197         }
43198         
43199         
43200         v = Math.max(1, v+adjust);
43201         
43202         this.execCmd('FontSize', v  );
43203     },
43204
43205     onEditorEvent : function(e)
43206     {
43207         this.owner.fireEvent('editorevent', this, e);
43208       //  this.updateToolbar();
43209         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43210     },
43211
43212     insertTag : function(tg)
43213     {
43214         // could be a bit smarter... -> wrap the current selected tRoo..
43215         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43216             
43217             range = this.createRange(this.getSelection());
43218             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43219             wrappingNode.appendChild(range.extractContents());
43220             range.insertNode(wrappingNode);
43221
43222             return;
43223             
43224             
43225             
43226         }
43227         this.execCmd("formatblock",   tg);
43228         
43229     },
43230     
43231     insertText : function(txt)
43232     {
43233         
43234         
43235         var range = this.createRange();
43236         range.deleteContents();
43237                //alert(Sender.getAttribute('label'));
43238                
43239         range.insertNode(this.doc.createTextNode(txt));
43240     } ,
43241     
43242      
43243
43244     /**
43245      * Executes a Midas editor command on the editor document and performs necessary focus and
43246      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43247      * @param {String} cmd The Midas command
43248      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43249      */
43250     relayCmd : function(cmd, value){
43251         this.win.focus();
43252         this.execCmd(cmd, value);
43253         this.owner.fireEvent('editorevent', this);
43254         //this.updateToolbar();
43255         this.owner.deferFocus();
43256     },
43257
43258     /**
43259      * Executes a Midas editor command directly on the editor document.
43260      * For visual commands, you should use {@link #relayCmd} instead.
43261      * <b>This should only be called after the editor is initialized.</b>
43262      * @param {String} cmd The Midas command
43263      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43264      */
43265     execCmd : function(cmd, value){
43266         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43267         this.syncValue();
43268     },
43269  
43270  
43271    
43272     /**
43273      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43274      * to insert tRoo.
43275      * @param {String} text | dom node.. 
43276      */
43277     insertAtCursor : function(text)
43278     {
43279         
43280         if(!this.activated){
43281             return;
43282         }
43283         /*
43284         if(Roo.isIE){
43285             this.win.focus();
43286             var r = this.doc.selection.createRange();
43287             if(r){
43288                 r.collapse(true);
43289                 r.pasteHTML(text);
43290                 this.syncValue();
43291                 this.deferFocus();
43292             
43293             }
43294             return;
43295         }
43296         */
43297         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43298             this.win.focus();
43299             
43300             
43301             // from jquery ui (MIT licenced)
43302             var range, node;
43303             var win = this.win;
43304             
43305             if (win.getSelection && win.getSelection().getRangeAt) {
43306                 range = win.getSelection().getRangeAt(0);
43307                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43308                 range.insertNode(node);
43309             } else if (win.document.selection && win.document.selection.createRange) {
43310                 // no firefox support
43311                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43312                 win.document.selection.createRange().pasteHTML(txt);
43313             } else {
43314                 // no firefox support
43315                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43316                 this.execCmd('InsertHTML', txt);
43317             } 
43318             
43319             this.syncValue();
43320             
43321             this.deferFocus();
43322         }
43323     },
43324  // private
43325     mozKeyPress : function(e){
43326         if(e.ctrlKey){
43327             var c = e.getCharCode(), cmd;
43328           
43329             if(c > 0){
43330                 c = String.fromCharCode(c).toLowerCase();
43331                 switch(c){
43332                     case 'b':
43333                         cmd = 'bold';
43334                         break;
43335                     case 'i':
43336                         cmd = 'italic';
43337                         break;
43338                     
43339                     case 'u':
43340                         cmd = 'underline';
43341                         break;
43342                     
43343                     case 'v':
43344                         this.cleanUpPaste.defer(100, this);
43345                         return;
43346                         
43347                 }
43348                 if(cmd){
43349                     this.win.focus();
43350                     this.execCmd(cmd);
43351                     this.deferFocus();
43352                     e.preventDefault();
43353                 }
43354                 
43355             }
43356         }
43357     },
43358
43359     // private
43360     fixKeys : function(){ // load time branching for fastest keydown performance
43361         if(Roo.isIE){
43362             return function(e){
43363                 var k = e.getKey(), r;
43364                 if(k == e.TAB){
43365                     e.stopEvent();
43366                     r = this.doc.selection.createRange();
43367                     if(r){
43368                         r.collapse(true);
43369                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43370                         this.deferFocus();
43371                     }
43372                     return;
43373                 }
43374                 
43375                 if(k == e.ENTER){
43376                     r = this.doc.selection.createRange();
43377                     if(r){
43378                         var target = r.parentElement();
43379                         if(!target || target.tagName.toLowerCase() != 'li'){
43380                             e.stopEvent();
43381                             r.pasteHTML('<br />');
43382                             r.collapse(false);
43383                             r.select();
43384                         }
43385                     }
43386                 }
43387                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43388                     this.cleanUpPaste.defer(100, this);
43389                     return;
43390                 }
43391                 
43392                 
43393             };
43394         }else if(Roo.isOpera){
43395             return function(e){
43396                 var k = e.getKey();
43397                 if(k == e.TAB){
43398                     e.stopEvent();
43399                     this.win.focus();
43400                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43401                     this.deferFocus();
43402                 }
43403                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43404                     this.cleanUpPaste.defer(100, this);
43405                     return;
43406                 }
43407                 
43408             };
43409         }else if(Roo.isSafari){
43410             return function(e){
43411                 var k = e.getKey();
43412                 
43413                 if(k == e.TAB){
43414                     e.stopEvent();
43415                     this.execCmd('InsertText','\t');
43416                     this.deferFocus();
43417                     return;
43418                 }
43419                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43420                     this.cleanUpPaste.defer(100, this);
43421                     return;
43422                 }
43423                 
43424              };
43425         }
43426     }(),
43427     
43428     getAllAncestors: function()
43429     {
43430         var p = this.getSelectedNode();
43431         var a = [];
43432         if (!p) {
43433             a.push(p); // push blank onto stack..
43434             p = this.getParentElement();
43435         }
43436         
43437         
43438         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43439             a.push(p);
43440             p = p.parentNode;
43441         }
43442         a.push(this.doc.body);
43443         return a;
43444     },
43445     lastSel : false,
43446     lastSelNode : false,
43447     
43448     
43449     getSelection : function() 
43450     {
43451         this.assignDocWin();
43452         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43453     },
43454     
43455     getSelectedNode: function() 
43456     {
43457         // this may only work on Gecko!!!
43458         
43459         // should we cache this!!!!
43460         
43461         
43462         
43463          
43464         var range = this.createRange(this.getSelection()).cloneRange();
43465         
43466         if (Roo.isIE) {
43467             var parent = range.parentElement();
43468             while (true) {
43469                 var testRange = range.duplicate();
43470                 testRange.moveToElementText(parent);
43471                 if (testRange.inRange(range)) {
43472                     break;
43473                 }
43474                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43475                     break;
43476                 }
43477                 parent = parent.parentElement;
43478             }
43479             return parent;
43480         }
43481         
43482         // is ancestor a text element.
43483         var ac =  range.commonAncestorContainer;
43484         if (ac.nodeType == 3) {
43485             ac = ac.parentNode;
43486         }
43487         
43488         var ar = ac.childNodes;
43489          
43490         var nodes = [];
43491         var other_nodes = [];
43492         var has_other_nodes = false;
43493         for (var i=0;i<ar.length;i++) {
43494             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43495                 continue;
43496             }
43497             // fullly contained node.
43498             
43499             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43500                 nodes.push(ar[i]);
43501                 continue;
43502             }
43503             
43504             // probably selected..
43505             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43506                 other_nodes.push(ar[i]);
43507                 continue;
43508             }
43509             // outer..
43510             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43511                 continue;
43512             }
43513             
43514             
43515             has_other_nodes = true;
43516         }
43517         if (!nodes.length && other_nodes.length) {
43518             nodes= other_nodes;
43519         }
43520         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43521             return false;
43522         }
43523         
43524         return nodes[0];
43525     },
43526     createRange: function(sel)
43527     {
43528         // this has strange effects when using with 
43529         // top toolbar - not sure if it's a great idea.
43530         //this.editor.contentWindow.focus();
43531         if (typeof sel != "undefined") {
43532             try {
43533                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43534             } catch(e) {
43535                 return this.doc.createRange();
43536             }
43537         } else {
43538             return this.doc.createRange();
43539         }
43540     },
43541     getParentElement: function()
43542     {
43543         
43544         this.assignDocWin();
43545         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43546         
43547         var range = this.createRange(sel);
43548          
43549         try {
43550             var p = range.commonAncestorContainer;
43551             while (p.nodeType == 3) { // text node
43552                 p = p.parentNode;
43553             }
43554             return p;
43555         } catch (e) {
43556             return null;
43557         }
43558     
43559     },
43560     /***
43561      *
43562      * Range intersection.. the hard stuff...
43563      *  '-1' = before
43564      *  '0' = hits..
43565      *  '1' = after.
43566      *         [ -- selected range --- ]
43567      *   [fail]                        [fail]
43568      *
43569      *    basically..
43570      *      if end is before start or  hits it. fail.
43571      *      if start is after end or hits it fail.
43572      *
43573      *   if either hits (but other is outside. - then it's not 
43574      *   
43575      *    
43576      **/
43577     
43578     
43579     // @see http://www.thismuchiknow.co.uk/?p=64.
43580     rangeIntersectsNode : function(range, node)
43581     {
43582         var nodeRange = node.ownerDocument.createRange();
43583         try {
43584             nodeRange.selectNode(node);
43585         } catch (e) {
43586             nodeRange.selectNodeContents(node);
43587         }
43588     
43589         var rangeStartRange = range.cloneRange();
43590         rangeStartRange.collapse(true);
43591     
43592         var rangeEndRange = range.cloneRange();
43593         rangeEndRange.collapse(false);
43594     
43595         var nodeStartRange = nodeRange.cloneRange();
43596         nodeStartRange.collapse(true);
43597     
43598         var nodeEndRange = nodeRange.cloneRange();
43599         nodeEndRange.collapse(false);
43600     
43601         return rangeStartRange.compareBoundaryPoints(
43602                  Range.START_TO_START, nodeEndRange) == -1 &&
43603                rangeEndRange.compareBoundaryPoints(
43604                  Range.START_TO_START, nodeStartRange) == 1;
43605         
43606          
43607     },
43608     rangeCompareNode : function(range, node)
43609     {
43610         var nodeRange = node.ownerDocument.createRange();
43611         try {
43612             nodeRange.selectNode(node);
43613         } catch (e) {
43614             nodeRange.selectNodeContents(node);
43615         }
43616         
43617         
43618         range.collapse(true);
43619     
43620         nodeRange.collapse(true);
43621      
43622         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43623         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43624          
43625         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43626         
43627         var nodeIsBefore   =  ss == 1;
43628         var nodeIsAfter    = ee == -1;
43629         
43630         if (nodeIsBefore && nodeIsAfter) {
43631             return 0; // outer
43632         }
43633         if (!nodeIsBefore && nodeIsAfter) {
43634             return 1; //right trailed.
43635         }
43636         
43637         if (nodeIsBefore && !nodeIsAfter) {
43638             return 2;  // left trailed.
43639         }
43640         // fully contined.
43641         return 3;
43642     },
43643
43644     // private? - in a new class?
43645     cleanUpPaste :  function()
43646     {
43647         // cleans up the whole document..
43648         Roo.log('cleanuppaste');
43649         
43650         this.cleanUpChildren(this.doc.body);
43651         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43652         if (clean != this.doc.body.innerHTML) {
43653             this.doc.body.innerHTML = clean;
43654         }
43655         
43656     },
43657     
43658     cleanWordChars : function(input) {// change the chars to hex code
43659         var he = Roo.HtmlEditorCore;
43660         
43661         var output = input;
43662         Roo.each(he.swapCodes, function(sw) { 
43663             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43664             
43665             output = output.replace(swapper, sw[1]);
43666         });
43667         
43668         return output;
43669     },
43670     
43671     
43672     cleanUpChildren : function (n)
43673     {
43674         if (!n.childNodes.length) {
43675             return;
43676         }
43677         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43678            this.cleanUpChild(n.childNodes[i]);
43679         }
43680     },
43681     
43682     
43683         
43684     
43685     cleanUpChild : function (node)
43686     {
43687         var ed = this;
43688         //console.log(node);
43689         if (node.nodeName == "#text") {
43690             // clean up silly Windows -- stuff?
43691             return; 
43692         }
43693         if (node.nodeName == "#comment") {
43694             node.parentNode.removeChild(node);
43695             // clean up silly Windows -- stuff?
43696             return; 
43697         }
43698         var lcname = node.tagName.toLowerCase();
43699         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43700         // whitelist of tags..
43701         
43702         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43703             // remove node.
43704             node.parentNode.removeChild(node);
43705             return;
43706             
43707         }
43708         
43709         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43710         
43711         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43712         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43713         
43714         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43715         //    remove_keep_children = true;
43716         //}
43717         
43718         if (remove_keep_children) {
43719             this.cleanUpChildren(node);
43720             // inserts everything just before this node...
43721             while (node.childNodes.length) {
43722                 var cn = node.childNodes[0];
43723                 node.removeChild(cn);
43724                 node.parentNode.insertBefore(cn, node);
43725             }
43726             node.parentNode.removeChild(node);
43727             return;
43728         }
43729         
43730         if (!node.attributes || !node.attributes.length) {
43731             this.cleanUpChildren(node);
43732             return;
43733         }
43734         
43735         function cleanAttr(n,v)
43736         {
43737             
43738             if (v.match(/^\./) || v.match(/^\//)) {
43739                 return;
43740             }
43741             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43742                 return;
43743             }
43744             if (v.match(/^#/)) {
43745                 return;
43746             }
43747 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43748             node.removeAttribute(n);
43749             
43750         }
43751         
43752         var cwhite = this.cwhite;
43753         var cblack = this.cblack;
43754             
43755         function cleanStyle(n,v)
43756         {
43757             if (v.match(/expression/)) { //XSS?? should we even bother..
43758                 node.removeAttribute(n);
43759                 return;
43760             }
43761             
43762             var parts = v.split(/;/);
43763             var clean = [];
43764             
43765             Roo.each(parts, function(p) {
43766                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43767                 if (!p.length) {
43768                     return true;
43769                 }
43770                 var l = p.split(':').shift().replace(/\s+/g,'');
43771                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43772                 
43773                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43774 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43775                     //node.removeAttribute(n);
43776                     return true;
43777                 }
43778                 //Roo.log()
43779                 // only allow 'c whitelisted system attributes'
43780                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43781 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43782                     //node.removeAttribute(n);
43783                     return true;
43784                 }
43785                 
43786                 
43787                  
43788                 
43789                 clean.push(p);
43790                 return true;
43791             });
43792             if (clean.length) { 
43793                 node.setAttribute(n, clean.join(';'));
43794             } else {
43795                 node.removeAttribute(n);
43796             }
43797             
43798         }
43799         
43800         
43801         for (var i = node.attributes.length-1; i > -1 ; i--) {
43802             var a = node.attributes[i];
43803             //console.log(a);
43804             
43805             if (a.name.toLowerCase().substr(0,2)=='on')  {
43806                 node.removeAttribute(a.name);
43807                 continue;
43808             }
43809             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43810                 node.removeAttribute(a.name);
43811                 continue;
43812             }
43813             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43814                 cleanAttr(a.name,a.value); // fixme..
43815                 continue;
43816             }
43817             if (a.name == 'style') {
43818                 cleanStyle(a.name,a.value);
43819                 continue;
43820             }
43821             /// clean up MS crap..
43822             // tecnically this should be a list of valid class'es..
43823             
43824             
43825             if (a.name == 'class') {
43826                 if (a.value.match(/^Mso/)) {
43827                     node.className = '';
43828                 }
43829                 
43830                 if (a.value.match(/^body$/)) {
43831                     node.className = '';
43832                 }
43833                 continue;
43834             }
43835             
43836             // style cleanup!?
43837             // class cleanup?
43838             
43839         }
43840         
43841         
43842         this.cleanUpChildren(node);
43843         
43844         
43845     },
43846     
43847     /**
43848      * Clean up MS wordisms...
43849      */
43850     cleanWord : function(node)
43851     {
43852         
43853         
43854         if (!node) {
43855             this.cleanWord(this.doc.body);
43856             return;
43857         }
43858         if (node.nodeName == "#text") {
43859             // clean up silly Windows -- stuff?
43860             return; 
43861         }
43862         if (node.nodeName == "#comment") {
43863             node.parentNode.removeChild(node);
43864             // clean up silly Windows -- stuff?
43865             return; 
43866         }
43867         
43868         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43869             node.parentNode.removeChild(node);
43870             return;
43871         }
43872         
43873         // remove - but keep children..
43874         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43875             while (node.childNodes.length) {
43876                 var cn = node.childNodes[0];
43877                 node.removeChild(cn);
43878                 node.parentNode.insertBefore(cn, node);
43879             }
43880             node.parentNode.removeChild(node);
43881             this.iterateChildren(node, this.cleanWord);
43882             return;
43883         }
43884         // clean styles
43885         if (node.className.length) {
43886             
43887             var cn = node.className.split(/\W+/);
43888             var cna = [];
43889             Roo.each(cn, function(cls) {
43890                 if (cls.match(/Mso[a-zA-Z]+/)) {
43891                     return;
43892                 }
43893                 cna.push(cls);
43894             });
43895             node.className = cna.length ? cna.join(' ') : '';
43896             if (!cna.length) {
43897                 node.removeAttribute("class");
43898             }
43899         }
43900         
43901         if (node.hasAttribute("lang")) {
43902             node.removeAttribute("lang");
43903         }
43904         
43905         if (node.hasAttribute("style")) {
43906             
43907             var styles = node.getAttribute("style").split(";");
43908             var nstyle = [];
43909             Roo.each(styles, function(s) {
43910                 if (!s.match(/:/)) {
43911                     return;
43912                 }
43913                 var kv = s.split(":");
43914                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43915                     return;
43916                 }
43917                 // what ever is left... we allow.
43918                 nstyle.push(s);
43919             });
43920             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43921             if (!nstyle.length) {
43922                 node.removeAttribute('style');
43923             }
43924         }
43925         this.iterateChildren(node, this.cleanWord);
43926         
43927         
43928         
43929     },
43930     /**
43931      * iterateChildren of a Node, calling fn each time, using this as the scole..
43932      * @param {DomNode} node node to iterate children of.
43933      * @param {Function} fn method of this class to call on each item.
43934      */
43935     iterateChildren : function(node, fn)
43936     {
43937         if (!node.childNodes.length) {
43938                 return;
43939         }
43940         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43941            fn.call(this, node.childNodes[i])
43942         }
43943     },
43944     
43945     
43946     /**
43947      * cleanTableWidths.
43948      *
43949      * Quite often pasting from word etc.. results in tables with column and widths.
43950      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43951      *
43952      */
43953     cleanTableWidths : function(node)
43954     {
43955          
43956          
43957         if (!node) {
43958             this.cleanTableWidths(this.doc.body);
43959             return;
43960         }
43961         
43962         // ignore list...
43963         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43964             return; 
43965         }
43966         Roo.log(node.tagName);
43967         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43968             this.iterateChildren(node, this.cleanTableWidths);
43969             return;
43970         }
43971         if (node.hasAttribute('width')) {
43972             node.removeAttribute('width');
43973         }
43974         
43975          
43976         if (node.hasAttribute("style")) {
43977             // pretty basic...
43978             
43979             var styles = node.getAttribute("style").split(";");
43980             var nstyle = [];
43981             Roo.each(styles, function(s) {
43982                 if (!s.match(/:/)) {
43983                     return;
43984                 }
43985                 var kv = s.split(":");
43986                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43987                     return;
43988                 }
43989                 // what ever is left... we allow.
43990                 nstyle.push(s);
43991             });
43992             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43993             if (!nstyle.length) {
43994                 node.removeAttribute('style');
43995             }
43996         }
43997         
43998         this.iterateChildren(node, this.cleanTableWidths);
43999         
44000         
44001     },
44002     
44003     
44004     
44005     
44006     domToHTML : function(currentElement, depth, nopadtext) {
44007         
44008         depth = depth || 0;
44009         nopadtext = nopadtext || false;
44010     
44011         if (!currentElement) {
44012             return this.domToHTML(this.doc.body);
44013         }
44014         
44015         //Roo.log(currentElement);
44016         var j;
44017         var allText = false;
44018         var nodeName = currentElement.nodeName;
44019         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44020         
44021         if  (nodeName == '#text') {
44022             
44023             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44024         }
44025         
44026         
44027         var ret = '';
44028         if (nodeName != 'BODY') {
44029              
44030             var i = 0;
44031             // Prints the node tagName, such as <A>, <IMG>, etc
44032             if (tagName) {
44033                 var attr = [];
44034                 for(i = 0; i < currentElement.attributes.length;i++) {
44035                     // quoting?
44036                     var aname = currentElement.attributes.item(i).name;
44037                     if (!currentElement.attributes.item(i).value.length) {
44038                         continue;
44039                     }
44040                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44041                 }
44042                 
44043                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44044             } 
44045             else {
44046                 
44047                 // eack
44048             }
44049         } else {
44050             tagName = false;
44051         }
44052         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44053             return ret;
44054         }
44055         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44056             nopadtext = true;
44057         }
44058         
44059         
44060         // Traverse the tree
44061         i = 0;
44062         var currentElementChild = currentElement.childNodes.item(i);
44063         var allText = true;
44064         var innerHTML  = '';
44065         lastnode = '';
44066         while (currentElementChild) {
44067             // Formatting code (indent the tree so it looks nice on the screen)
44068             var nopad = nopadtext;
44069             if (lastnode == 'SPAN') {
44070                 nopad  = true;
44071             }
44072             // text
44073             if  (currentElementChild.nodeName == '#text') {
44074                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44075                 toadd = nopadtext ? toadd : toadd.trim();
44076                 if (!nopad && toadd.length > 80) {
44077                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44078                 }
44079                 innerHTML  += toadd;
44080                 
44081                 i++;
44082                 currentElementChild = currentElement.childNodes.item(i);
44083                 lastNode = '';
44084                 continue;
44085             }
44086             allText = false;
44087             
44088             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44089                 
44090             // Recursively traverse the tree structure of the child node
44091             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44092             lastnode = currentElementChild.nodeName;
44093             i++;
44094             currentElementChild=currentElement.childNodes.item(i);
44095         }
44096         
44097         ret += innerHTML;
44098         
44099         if (!allText) {
44100                 // The remaining code is mostly for formatting the tree
44101             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44102         }
44103         
44104         
44105         if (tagName) {
44106             ret+= "</"+tagName+">";
44107         }
44108         return ret;
44109         
44110     },
44111         
44112     applyBlacklists : function()
44113     {
44114         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44115         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44116         
44117         this.white = [];
44118         this.black = [];
44119         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44120             if (b.indexOf(tag) > -1) {
44121                 return;
44122             }
44123             this.white.push(tag);
44124             
44125         }, this);
44126         
44127         Roo.each(w, function(tag) {
44128             if (b.indexOf(tag) > -1) {
44129                 return;
44130             }
44131             if (this.white.indexOf(tag) > -1) {
44132                 return;
44133             }
44134             this.white.push(tag);
44135             
44136         }, this);
44137         
44138         
44139         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44140             if (w.indexOf(tag) > -1) {
44141                 return;
44142             }
44143             this.black.push(tag);
44144             
44145         }, this);
44146         
44147         Roo.each(b, function(tag) {
44148             if (w.indexOf(tag) > -1) {
44149                 return;
44150             }
44151             if (this.black.indexOf(tag) > -1) {
44152                 return;
44153             }
44154             this.black.push(tag);
44155             
44156         }, this);
44157         
44158         
44159         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44160         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44161         
44162         this.cwhite = [];
44163         this.cblack = [];
44164         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44165             if (b.indexOf(tag) > -1) {
44166                 return;
44167             }
44168             this.cwhite.push(tag);
44169             
44170         }, this);
44171         
44172         Roo.each(w, function(tag) {
44173             if (b.indexOf(tag) > -1) {
44174                 return;
44175             }
44176             if (this.cwhite.indexOf(tag) > -1) {
44177                 return;
44178             }
44179             this.cwhite.push(tag);
44180             
44181         }, this);
44182         
44183         
44184         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44185             if (w.indexOf(tag) > -1) {
44186                 return;
44187             }
44188             this.cblack.push(tag);
44189             
44190         }, this);
44191         
44192         Roo.each(b, function(tag) {
44193             if (w.indexOf(tag) > -1) {
44194                 return;
44195             }
44196             if (this.cblack.indexOf(tag) > -1) {
44197                 return;
44198             }
44199             this.cblack.push(tag);
44200             
44201         }, this);
44202     },
44203     
44204     setStylesheets : function(stylesheets)
44205     {
44206         if(typeof(stylesheets) == 'string'){
44207             Roo.get(this.iframe.contentDocument.head).createChild({
44208                 tag : 'link',
44209                 rel : 'stylesheet',
44210                 type : 'text/css',
44211                 href : stylesheets
44212             });
44213             
44214             return;
44215         }
44216         var _this = this;
44217      
44218         Roo.each(stylesheets, function(s) {
44219             if(!s.length){
44220                 return;
44221             }
44222             
44223             Roo.get(_this.iframe.contentDocument.head).createChild({
44224                 tag : 'link',
44225                 rel : 'stylesheet',
44226                 type : 'text/css',
44227                 href : s
44228             });
44229         });
44230
44231         
44232     },
44233     
44234     removeStylesheets : function()
44235     {
44236         var _this = this;
44237         
44238         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44239             s.remove();
44240         });
44241     },
44242     
44243     setStyle : function(style)
44244     {
44245         Roo.get(this.iframe.contentDocument.head).createChild({
44246             tag : 'style',
44247             type : 'text/css',
44248             html : style
44249         });
44250
44251         return;
44252     }
44253     
44254     // hide stuff that is not compatible
44255     /**
44256      * @event blur
44257      * @hide
44258      */
44259     /**
44260      * @event change
44261      * @hide
44262      */
44263     /**
44264      * @event focus
44265      * @hide
44266      */
44267     /**
44268      * @event specialkey
44269      * @hide
44270      */
44271     /**
44272      * @cfg {String} fieldClass @hide
44273      */
44274     /**
44275      * @cfg {String} focusClass @hide
44276      */
44277     /**
44278      * @cfg {String} autoCreate @hide
44279      */
44280     /**
44281      * @cfg {String} inputType @hide
44282      */
44283     /**
44284      * @cfg {String} invalidClass @hide
44285      */
44286     /**
44287      * @cfg {String} invalidText @hide
44288      */
44289     /**
44290      * @cfg {String} msgFx @hide
44291      */
44292     /**
44293      * @cfg {String} validateOnBlur @hide
44294      */
44295 });
44296
44297 Roo.HtmlEditorCore.white = [
44298         'area', 'br', 'img', 'input', 'hr', 'wbr',
44299         
44300        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44301        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44302        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44303        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44304        'table',   'ul',         'xmp', 
44305        
44306        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44307       'thead',   'tr', 
44308      
44309       'dir', 'menu', 'ol', 'ul', 'dl',
44310        
44311       'embed',  'object'
44312 ];
44313
44314
44315 Roo.HtmlEditorCore.black = [
44316     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44317         'applet', // 
44318         'base',   'basefont', 'bgsound', 'blink',  'body', 
44319         'frame',  'frameset', 'head',    'html',   'ilayer', 
44320         'iframe', 'layer',  'link',     'meta',    'object',   
44321         'script', 'style' ,'title',  'xml' // clean later..
44322 ];
44323 Roo.HtmlEditorCore.clean = [
44324     'script', 'style', 'title', 'xml'
44325 ];
44326 Roo.HtmlEditorCore.remove = [
44327     'font'
44328 ];
44329 // attributes..
44330
44331 Roo.HtmlEditorCore.ablack = [
44332     'on'
44333 ];
44334     
44335 Roo.HtmlEditorCore.aclean = [ 
44336     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44337 ];
44338
44339 // protocols..
44340 Roo.HtmlEditorCore.pwhite= [
44341         'http',  'https',  'mailto'
44342 ];
44343
44344 // white listed style attributes.
44345 Roo.HtmlEditorCore.cwhite= [
44346       //  'text-align', /// default is to allow most things..
44347       
44348          
44349 //        'font-size'//??
44350 ];
44351
44352 // black listed style attributes.
44353 Roo.HtmlEditorCore.cblack= [
44354       //  'font-size' -- this can be set by the project 
44355 ];
44356
44357
44358 Roo.HtmlEditorCore.swapCodes   =[ 
44359     [    8211, "--" ], 
44360     [    8212, "--" ], 
44361     [    8216,  "'" ],  
44362     [    8217, "'" ],  
44363     [    8220, '"' ],  
44364     [    8221, '"' ],  
44365     [    8226, "*" ],  
44366     [    8230, "..." ]
44367 ]; 
44368
44369     //<script type="text/javascript">
44370
44371 /*
44372  * Ext JS Library 1.1.1
44373  * Copyright(c) 2006-2007, Ext JS, LLC.
44374  * Licence LGPL
44375  * 
44376  */
44377  
44378  
44379 Roo.form.HtmlEditor = function(config){
44380     
44381     
44382     
44383     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44384     
44385     if (!this.toolbars) {
44386         this.toolbars = [];
44387     }
44388     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44389     
44390     
44391 };
44392
44393 /**
44394  * @class Roo.form.HtmlEditor
44395  * @extends Roo.form.Field
44396  * Provides a lightweight HTML Editor component.
44397  *
44398  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44399  * 
44400  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44401  * supported by this editor.</b><br/><br/>
44402  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44403  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44404  */
44405 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44406     /**
44407      * @cfg {Boolean} clearUp
44408      */
44409     clearUp : true,
44410       /**
44411      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44412      */
44413     toolbars : false,
44414    
44415      /**
44416      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44417      *                        Roo.resizable.
44418      */
44419     resizable : false,
44420      /**
44421      * @cfg {Number} height (in pixels)
44422      */   
44423     height: 300,
44424    /**
44425      * @cfg {Number} width (in pixels)
44426      */   
44427     width: 500,
44428     
44429     /**
44430      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44431      * 
44432      */
44433     stylesheets: false,
44434     
44435     
44436      /**
44437      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44438      * 
44439      */
44440     cblack: false,
44441     /**
44442      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44443      * 
44444      */
44445     cwhite: false,
44446     
44447      /**
44448      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44449      * 
44450      */
44451     black: false,
44452     /**
44453      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44454      * 
44455      */
44456     white: false,
44457     
44458     // id of frame..
44459     frameId: false,
44460     
44461     // private properties
44462     validationEvent : false,
44463     deferHeight: true,
44464     initialized : false,
44465     activated : false,
44466     
44467     onFocus : Roo.emptyFn,
44468     iframePad:3,
44469     hideMode:'offsets',
44470     
44471     actionMode : 'container', // defaults to hiding it...
44472     
44473     defaultAutoCreate : { // modified by initCompnoent..
44474         tag: "textarea",
44475         style:"width:500px;height:300px;",
44476         autocomplete: "new-password"
44477     },
44478
44479     // private
44480     initComponent : function(){
44481         this.addEvents({
44482             /**
44483              * @event initialize
44484              * Fires when the editor is fully initialized (including the iframe)
44485              * @param {HtmlEditor} this
44486              */
44487             initialize: true,
44488             /**
44489              * @event activate
44490              * Fires when the editor is first receives the focus. Any insertion must wait
44491              * until after this event.
44492              * @param {HtmlEditor} this
44493              */
44494             activate: true,
44495              /**
44496              * @event beforesync
44497              * Fires before the textarea is updated with content from the editor iframe. Return false
44498              * to cancel the sync.
44499              * @param {HtmlEditor} this
44500              * @param {String} html
44501              */
44502             beforesync: true,
44503              /**
44504              * @event beforepush
44505              * Fires before the iframe editor is updated with content from the textarea. Return false
44506              * to cancel the push.
44507              * @param {HtmlEditor} this
44508              * @param {String} html
44509              */
44510             beforepush: true,
44511              /**
44512              * @event sync
44513              * Fires when the textarea is updated with content from the editor iframe.
44514              * @param {HtmlEditor} this
44515              * @param {String} html
44516              */
44517             sync: true,
44518              /**
44519              * @event push
44520              * Fires when the iframe editor is updated with content from the textarea.
44521              * @param {HtmlEditor} this
44522              * @param {String} html
44523              */
44524             push: true,
44525              /**
44526              * @event editmodechange
44527              * Fires when the editor switches edit modes
44528              * @param {HtmlEditor} this
44529              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44530              */
44531             editmodechange: true,
44532             /**
44533              * @event editorevent
44534              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44535              * @param {HtmlEditor} this
44536              */
44537             editorevent: true,
44538             /**
44539              * @event firstfocus
44540              * Fires when on first focus - needed by toolbars..
44541              * @param {HtmlEditor} this
44542              */
44543             firstfocus: true,
44544             /**
44545              * @event autosave
44546              * Auto save the htmlEditor value as a file into Events
44547              * @param {HtmlEditor} this
44548              */
44549             autosave: true,
44550             /**
44551              * @event savedpreview
44552              * preview the saved version of htmlEditor
44553              * @param {HtmlEditor} this
44554              */
44555             savedpreview: true,
44556             
44557             /**
44558             * @event stylesheetsclick
44559             * Fires when press the Sytlesheets button
44560             * @param {Roo.HtmlEditorCore} this
44561             */
44562             stylesheetsclick: true
44563         });
44564         this.defaultAutoCreate =  {
44565             tag: "textarea",
44566             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44567             autocomplete: "new-password"
44568         };
44569     },
44570
44571     /**
44572      * Protected method that will not generally be called directly. It
44573      * is called when the editor creates its toolbar. Override this method if you need to
44574      * add custom toolbar buttons.
44575      * @param {HtmlEditor} editor
44576      */
44577     createToolbar : function(editor){
44578         Roo.log("create toolbars");
44579         if (!editor.toolbars || !editor.toolbars.length) {
44580             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44581         }
44582         
44583         for (var i =0 ; i < editor.toolbars.length;i++) {
44584             editor.toolbars[i] = Roo.factory(
44585                     typeof(editor.toolbars[i]) == 'string' ?
44586                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44587                 Roo.form.HtmlEditor);
44588             editor.toolbars[i].init(editor);
44589         }
44590          
44591         
44592     },
44593
44594      
44595     // private
44596     onRender : function(ct, position)
44597     {
44598         var _t = this;
44599         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44600         
44601         this.wrap = this.el.wrap({
44602             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44603         });
44604         
44605         this.editorcore.onRender(ct, position);
44606          
44607         if (this.resizable) {
44608             this.resizeEl = new Roo.Resizable(this.wrap, {
44609                 pinned : true,
44610                 wrap: true,
44611                 dynamic : true,
44612                 minHeight : this.height,
44613                 height: this.height,
44614                 handles : this.resizable,
44615                 width: this.width,
44616                 listeners : {
44617                     resize : function(r, w, h) {
44618                         _t.onResize(w,h); // -something
44619                     }
44620                 }
44621             });
44622             
44623         }
44624         this.createToolbar(this);
44625        
44626         
44627         if(!this.width){
44628             this.setSize(this.wrap.getSize());
44629         }
44630         if (this.resizeEl) {
44631             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44632             // should trigger onReize..
44633         }
44634         
44635         this.keyNav = new Roo.KeyNav(this.el, {
44636             
44637             "tab" : function(e){
44638                 e.preventDefault();
44639                 
44640                 var value = this.getValue();
44641                 
44642                 var start = this.el.dom.selectionStart;
44643                 var end = this.el.dom.selectionEnd;
44644                 
44645                 if(!e.shiftKey){
44646                     
44647                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44648                     this.el.dom.setSelectionRange(end + 1, end + 1);
44649                     return;
44650                 }
44651                 
44652                 var f = value.substring(0, start).split("\t");
44653                 
44654                 if(f.pop().length != 0){
44655                     return;
44656                 }
44657                 
44658                 this.setValue(f.join("\t") + value.substring(end));
44659                 this.el.dom.setSelectionRange(start - 1, start - 1);
44660                 
44661             },
44662             
44663             "home" : function(e){
44664                 e.preventDefault();
44665                 
44666                 var curr = this.el.dom.selectionStart;
44667                 var lines = this.getValue().split("\n");
44668                 
44669                 if(!lines.length){
44670                     return;
44671                 }
44672                 
44673                 if(e.ctrlKey){
44674                     this.el.dom.setSelectionRange(0, 0);
44675                     return;
44676                 }
44677                 
44678                 var pos = 0;
44679                 
44680                 for (var i = 0; i < lines.length;i++) {
44681                     pos += lines[i].length;
44682                     
44683                     if(i != 0){
44684                         pos += 1;
44685                     }
44686                     
44687                     if(pos < curr){
44688                         continue;
44689                     }
44690                     
44691                     pos -= lines[i].length;
44692                     
44693                     break;
44694                 }
44695                 
44696                 if(!e.shiftKey){
44697                     this.el.dom.setSelectionRange(pos, pos);
44698                     return;
44699                 }
44700                 
44701                 this.el.dom.selectionStart = pos;
44702                 this.el.dom.selectionEnd = curr;
44703             },
44704             
44705             "end" : function(e){
44706                 e.preventDefault();
44707                 
44708                 var curr = this.el.dom.selectionStart;
44709                 var lines = this.getValue().split("\n");
44710                 
44711                 if(!lines.length){
44712                     return;
44713                 }
44714                 
44715                 if(e.ctrlKey){
44716                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44717                     return;
44718                 }
44719                 
44720                 var pos = 0;
44721                 
44722                 for (var i = 0; i < lines.length;i++) {
44723                     
44724                     pos += lines[i].length;
44725                     
44726                     if(i != 0){
44727                         pos += 1;
44728                     }
44729                     
44730                     if(pos < curr){
44731                         continue;
44732                     }
44733                     
44734                     break;
44735                 }
44736                 
44737                 if(!e.shiftKey){
44738                     this.el.dom.setSelectionRange(pos, pos);
44739                     return;
44740                 }
44741                 
44742                 this.el.dom.selectionStart = curr;
44743                 this.el.dom.selectionEnd = pos;
44744             },
44745
44746             scope : this,
44747
44748             doRelay : function(foo, bar, hname){
44749                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44750             },
44751
44752             forceKeyDown: true
44753         });
44754         
44755 //        if(this.autosave && this.w){
44756 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44757 //        }
44758     },
44759
44760     // private
44761     onResize : function(w, h)
44762     {
44763         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44764         var ew = false;
44765         var eh = false;
44766         
44767         if(this.el ){
44768             if(typeof w == 'number'){
44769                 var aw = w - this.wrap.getFrameWidth('lr');
44770                 this.el.setWidth(this.adjustWidth('textarea', aw));
44771                 ew = aw;
44772             }
44773             if(typeof h == 'number'){
44774                 var tbh = 0;
44775                 for (var i =0; i < this.toolbars.length;i++) {
44776                     // fixme - ask toolbars for heights?
44777                     tbh += this.toolbars[i].tb.el.getHeight();
44778                     if (this.toolbars[i].footer) {
44779                         tbh += this.toolbars[i].footer.el.getHeight();
44780                     }
44781                 }
44782                 
44783                 
44784                 
44785                 
44786                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44787                 ah -= 5; // knock a few pixes off for look..
44788 //                Roo.log(ah);
44789                 this.el.setHeight(this.adjustWidth('textarea', ah));
44790                 var eh = ah;
44791             }
44792         }
44793         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44794         this.editorcore.onResize(ew,eh);
44795         
44796     },
44797
44798     /**
44799      * Toggles the editor between standard and source edit mode.
44800      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44801      */
44802     toggleSourceEdit : function(sourceEditMode)
44803     {
44804         this.editorcore.toggleSourceEdit(sourceEditMode);
44805         
44806         if(this.editorcore.sourceEditMode){
44807             Roo.log('editor - showing textarea');
44808             
44809 //            Roo.log('in');
44810 //            Roo.log(this.syncValue());
44811             this.editorcore.syncValue();
44812             this.el.removeClass('x-hidden');
44813             this.el.dom.removeAttribute('tabIndex');
44814             this.el.focus();
44815             
44816             for (var i = 0; i < this.toolbars.length; i++) {
44817                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44818                     this.toolbars[i].tb.hide();
44819                     this.toolbars[i].footer.hide();
44820                 }
44821             }
44822             
44823         }else{
44824             Roo.log('editor - hiding textarea');
44825 //            Roo.log('out')
44826 //            Roo.log(this.pushValue()); 
44827             this.editorcore.pushValue();
44828             
44829             this.el.addClass('x-hidden');
44830             this.el.dom.setAttribute('tabIndex', -1);
44831             
44832             for (var i = 0; i < this.toolbars.length; i++) {
44833                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44834                     this.toolbars[i].tb.show();
44835                     this.toolbars[i].footer.show();
44836                 }
44837             }
44838             
44839             //this.deferFocus();
44840         }
44841         
44842         this.setSize(this.wrap.getSize());
44843         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44844         
44845         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44846     },
44847  
44848     // private (for BoxComponent)
44849     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44850
44851     // private (for BoxComponent)
44852     getResizeEl : function(){
44853         return this.wrap;
44854     },
44855
44856     // private (for BoxComponent)
44857     getPositionEl : function(){
44858         return this.wrap;
44859     },
44860
44861     // private
44862     initEvents : function(){
44863         this.originalValue = this.getValue();
44864     },
44865
44866     /**
44867      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44868      * @method
44869      */
44870     markInvalid : Roo.emptyFn,
44871     /**
44872      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44873      * @method
44874      */
44875     clearInvalid : Roo.emptyFn,
44876
44877     setValue : function(v){
44878         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44879         this.editorcore.pushValue();
44880     },
44881
44882      
44883     // private
44884     deferFocus : function(){
44885         this.focus.defer(10, this);
44886     },
44887
44888     // doc'ed in Field
44889     focus : function(){
44890         this.editorcore.focus();
44891         
44892     },
44893       
44894
44895     // private
44896     onDestroy : function(){
44897         
44898         
44899         
44900         if(this.rendered){
44901             
44902             for (var i =0; i < this.toolbars.length;i++) {
44903                 // fixme - ask toolbars for heights?
44904                 this.toolbars[i].onDestroy();
44905             }
44906             
44907             this.wrap.dom.innerHTML = '';
44908             this.wrap.remove();
44909         }
44910     },
44911
44912     // private
44913     onFirstFocus : function(){
44914         //Roo.log("onFirstFocus");
44915         this.editorcore.onFirstFocus();
44916          for (var i =0; i < this.toolbars.length;i++) {
44917             this.toolbars[i].onFirstFocus();
44918         }
44919         
44920     },
44921     
44922     // private
44923     syncValue : function()
44924     {
44925         this.editorcore.syncValue();
44926     },
44927     
44928     pushValue : function()
44929     {
44930         this.editorcore.pushValue();
44931     },
44932     
44933     setStylesheets : function(stylesheets)
44934     {
44935         this.editorcore.setStylesheets(stylesheets);
44936     },
44937     
44938     removeStylesheets : function()
44939     {
44940         this.editorcore.removeStylesheets();
44941     }
44942      
44943     
44944     // hide stuff that is not compatible
44945     /**
44946      * @event blur
44947      * @hide
44948      */
44949     /**
44950      * @event change
44951      * @hide
44952      */
44953     /**
44954      * @event focus
44955      * @hide
44956      */
44957     /**
44958      * @event specialkey
44959      * @hide
44960      */
44961     /**
44962      * @cfg {String} fieldClass @hide
44963      */
44964     /**
44965      * @cfg {String} focusClass @hide
44966      */
44967     /**
44968      * @cfg {String} autoCreate @hide
44969      */
44970     /**
44971      * @cfg {String} inputType @hide
44972      */
44973     /**
44974      * @cfg {String} invalidClass @hide
44975      */
44976     /**
44977      * @cfg {String} invalidText @hide
44978      */
44979     /**
44980      * @cfg {String} msgFx @hide
44981      */
44982     /**
44983      * @cfg {String} validateOnBlur @hide
44984      */
44985 });
44986  
44987     // <script type="text/javascript">
44988 /*
44989  * Based on
44990  * Ext JS Library 1.1.1
44991  * Copyright(c) 2006-2007, Ext JS, LLC.
44992  *  
44993  
44994  */
44995
44996 /**
44997  * @class Roo.form.HtmlEditorToolbar1
44998  * Basic Toolbar
44999  * 
45000  * Usage:
45001  *
45002  new Roo.form.HtmlEditor({
45003     ....
45004     toolbars : [
45005         new Roo.form.HtmlEditorToolbar1({
45006             disable : { fonts: 1 , format: 1, ..., ... , ...],
45007             btns : [ .... ]
45008         })
45009     }
45010      
45011  * 
45012  * @cfg {Object} disable List of elements to disable..
45013  * @cfg {Array} btns List of additional buttons.
45014  * 
45015  * 
45016  * NEEDS Extra CSS? 
45017  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45018  */
45019  
45020 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45021 {
45022     
45023     Roo.apply(this, config);
45024     
45025     // default disabled, based on 'good practice'..
45026     this.disable = this.disable || {};
45027     Roo.applyIf(this.disable, {
45028         fontSize : true,
45029         colors : true,
45030         specialElements : true
45031     });
45032     
45033     
45034     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45035     // dont call parent... till later.
45036 }
45037
45038 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45039     
45040     tb: false,
45041     
45042     rendered: false,
45043     
45044     editor : false,
45045     editorcore : false,
45046     /**
45047      * @cfg {Object} disable  List of toolbar elements to disable
45048          
45049      */
45050     disable : false,
45051     
45052     
45053      /**
45054      * @cfg {String} createLinkText The default text for the create link prompt
45055      */
45056     createLinkText : 'Please enter the URL for the link:',
45057     /**
45058      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45059      */
45060     defaultLinkValue : 'http:/'+'/',
45061    
45062     
45063       /**
45064      * @cfg {Array} fontFamilies An array of available font families
45065      */
45066     fontFamilies : [
45067         'Arial',
45068         'Courier New',
45069         'Tahoma',
45070         'Times New Roman',
45071         'Verdana'
45072     ],
45073     
45074     specialChars : [
45075            "&#169;",
45076           "&#174;",     
45077           "&#8482;",    
45078           "&#163;" ,    
45079          // "&#8212;",    
45080           "&#8230;",    
45081           "&#247;" ,    
45082         //  "&#225;" ,     ?? a acute?
45083            "&#8364;"    , //Euro
45084        //   "&#8220;"    ,
45085         //  "&#8221;"    ,
45086         //  "&#8226;"    ,
45087           "&#176;"  //   , // degrees
45088
45089          // "&#233;"     , // e ecute
45090          // "&#250;"     , // u ecute?
45091     ],
45092     
45093     specialElements : [
45094         {
45095             text: "Insert Table",
45096             xtype: 'MenuItem',
45097             xns : Roo.Menu,
45098             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45099                 
45100         },
45101         {    
45102             text: "Insert Image",
45103             xtype: 'MenuItem',
45104             xns : Roo.Menu,
45105             ihtml : '<img src="about:blank"/>'
45106             
45107         }
45108         
45109          
45110     ],
45111     
45112     
45113     inputElements : [ 
45114             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45115             "input:submit", "input:button", "select", "textarea", "label" ],
45116     formats : [
45117         ["p"] ,  
45118         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45119         ["pre"],[ "code"], 
45120         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45121         ['div'],['span']
45122     ],
45123     
45124     cleanStyles : [
45125         "font-size"
45126     ],
45127      /**
45128      * @cfg {String} defaultFont default font to use.
45129      */
45130     defaultFont: 'tahoma',
45131    
45132     fontSelect : false,
45133     
45134     
45135     formatCombo : false,
45136     
45137     init : function(editor)
45138     {
45139         this.editor = editor;
45140         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45141         var editorcore = this.editorcore;
45142         
45143         var _t = this;
45144         
45145         var fid = editorcore.frameId;
45146         var etb = this;
45147         function btn(id, toggle, handler){
45148             var xid = fid + '-'+ id ;
45149             return {
45150                 id : xid,
45151                 cmd : id,
45152                 cls : 'x-btn-icon x-edit-'+id,
45153                 enableToggle:toggle !== false,
45154                 scope: _t, // was editor...
45155                 handler:handler||_t.relayBtnCmd,
45156                 clickEvent:'mousedown',
45157                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45158                 tabIndex:-1
45159             };
45160         }
45161         
45162         
45163         
45164         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45165         this.tb = tb;
45166          // stop form submits
45167         tb.el.on('click', function(e){
45168             e.preventDefault(); // what does this do?
45169         });
45170
45171         if(!this.disable.font) { // && !Roo.isSafari){
45172             /* why no safari for fonts 
45173             editor.fontSelect = tb.el.createChild({
45174                 tag:'select',
45175                 tabIndex: -1,
45176                 cls:'x-font-select',
45177                 html: this.createFontOptions()
45178             });
45179             
45180             editor.fontSelect.on('change', function(){
45181                 var font = editor.fontSelect.dom.value;
45182                 editor.relayCmd('fontname', font);
45183                 editor.deferFocus();
45184             }, editor);
45185             
45186             tb.add(
45187                 editor.fontSelect.dom,
45188                 '-'
45189             );
45190             */
45191             
45192         };
45193         if(!this.disable.formats){
45194             this.formatCombo = new Roo.form.ComboBox({
45195                 store: new Roo.data.SimpleStore({
45196                     id : 'tag',
45197                     fields: ['tag'],
45198                     data : this.formats // from states.js
45199                 }),
45200                 blockFocus : true,
45201                 name : '',
45202                 //autoCreate : {tag: "div",  size: "20"},
45203                 displayField:'tag',
45204                 typeAhead: false,
45205                 mode: 'local',
45206                 editable : false,
45207                 triggerAction: 'all',
45208                 emptyText:'Add tag',
45209                 selectOnFocus:true,
45210                 width:135,
45211                 listeners : {
45212                     'select': function(c, r, i) {
45213                         editorcore.insertTag(r.get('tag'));
45214                         editor.focus();
45215                     }
45216                 }
45217
45218             });
45219             tb.addField(this.formatCombo);
45220             
45221         }
45222         
45223         if(!this.disable.format){
45224             tb.add(
45225                 btn('bold'),
45226                 btn('italic'),
45227                 btn('underline'),
45228                 btn('strikethrough')
45229             );
45230         };
45231         if(!this.disable.fontSize){
45232             tb.add(
45233                 '-',
45234                 
45235                 
45236                 btn('increasefontsize', false, editorcore.adjustFont),
45237                 btn('decreasefontsize', false, editorcore.adjustFont)
45238             );
45239         };
45240         
45241         
45242         if(!this.disable.colors){
45243             tb.add(
45244                 '-', {
45245                     id:editorcore.frameId +'-forecolor',
45246                     cls:'x-btn-icon x-edit-forecolor',
45247                     clickEvent:'mousedown',
45248                     tooltip: this.buttonTips['forecolor'] || undefined,
45249                     tabIndex:-1,
45250                     menu : new Roo.menu.ColorMenu({
45251                         allowReselect: true,
45252                         focus: Roo.emptyFn,
45253                         value:'000000',
45254                         plain:true,
45255                         selectHandler: function(cp, color){
45256                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45257                             editor.deferFocus();
45258                         },
45259                         scope: editorcore,
45260                         clickEvent:'mousedown'
45261                     })
45262                 }, {
45263                     id:editorcore.frameId +'backcolor',
45264                     cls:'x-btn-icon x-edit-backcolor',
45265                     clickEvent:'mousedown',
45266                     tooltip: this.buttonTips['backcolor'] || undefined,
45267                     tabIndex:-1,
45268                     menu : new Roo.menu.ColorMenu({
45269                         focus: Roo.emptyFn,
45270                         value:'FFFFFF',
45271                         plain:true,
45272                         allowReselect: true,
45273                         selectHandler: function(cp, color){
45274                             if(Roo.isGecko){
45275                                 editorcore.execCmd('useCSS', false);
45276                                 editorcore.execCmd('hilitecolor', color);
45277                                 editorcore.execCmd('useCSS', true);
45278                                 editor.deferFocus();
45279                             }else{
45280                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45281                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45282                                 editor.deferFocus();
45283                             }
45284                         },
45285                         scope:editorcore,
45286                         clickEvent:'mousedown'
45287                     })
45288                 }
45289             );
45290         };
45291         // now add all the items...
45292         
45293
45294         if(!this.disable.alignments){
45295             tb.add(
45296                 '-',
45297                 btn('justifyleft'),
45298                 btn('justifycenter'),
45299                 btn('justifyright')
45300             );
45301         };
45302
45303         //if(!Roo.isSafari){
45304             if(!this.disable.links){
45305                 tb.add(
45306                     '-',
45307                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45308                 );
45309             };
45310
45311             if(!this.disable.lists){
45312                 tb.add(
45313                     '-',
45314                     btn('insertorderedlist'),
45315                     btn('insertunorderedlist')
45316                 );
45317             }
45318             if(!this.disable.sourceEdit){
45319                 tb.add(
45320                     '-',
45321                     btn('sourceedit', true, function(btn){
45322                         this.toggleSourceEdit(btn.pressed);
45323                     })
45324                 );
45325             }
45326         //}
45327         
45328         var smenu = { };
45329         // special menu.. - needs to be tidied up..
45330         if (!this.disable.special) {
45331             smenu = {
45332                 text: "&#169;",
45333                 cls: 'x-edit-none',
45334                 
45335                 menu : {
45336                     items : []
45337                 }
45338             };
45339             for (var i =0; i < this.specialChars.length; i++) {
45340                 smenu.menu.items.push({
45341                     
45342                     html: this.specialChars[i],
45343                     handler: function(a,b) {
45344                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45345                         //editor.insertAtCursor(a.html);
45346                         
45347                     },
45348                     tabIndex:-1
45349                 });
45350             }
45351             
45352             
45353             tb.add(smenu);
45354             
45355             
45356         }
45357         
45358         var cmenu = { };
45359         if (!this.disable.cleanStyles) {
45360             cmenu = {
45361                 cls: 'x-btn-icon x-btn-clear',
45362                 
45363                 menu : {
45364                     items : []
45365                 }
45366             };
45367             for (var i =0; i < this.cleanStyles.length; i++) {
45368                 cmenu.menu.items.push({
45369                     actiontype : this.cleanStyles[i],
45370                     html: 'Remove ' + this.cleanStyles[i],
45371                     handler: function(a,b) {
45372 //                        Roo.log(a);
45373 //                        Roo.log(b);
45374                         var c = Roo.get(editorcore.doc.body);
45375                         c.select('[style]').each(function(s) {
45376                             s.dom.style.removeProperty(a.actiontype);
45377                         });
45378                         editorcore.syncValue();
45379                     },
45380                     tabIndex:-1
45381                 });
45382             }
45383              cmenu.menu.items.push({
45384                 actiontype : 'tablewidths',
45385                 html: 'Remove Table Widths',
45386                 handler: function(a,b) {
45387                     editorcore.cleanTableWidths();
45388                     editorcore.syncValue();
45389                 },
45390                 tabIndex:-1
45391             });
45392             cmenu.menu.items.push({
45393                 actiontype : 'word',
45394                 html: 'Remove MS Word Formating',
45395                 handler: function(a,b) {
45396                     editorcore.cleanWord();
45397                     editorcore.syncValue();
45398                 },
45399                 tabIndex:-1
45400             });
45401             
45402             cmenu.menu.items.push({
45403                 actiontype : 'all',
45404                 html: 'Remove All Styles',
45405                 handler: function(a,b) {
45406                     
45407                     var c = Roo.get(editorcore.doc.body);
45408                     c.select('[style]').each(function(s) {
45409                         s.dom.removeAttribute('style');
45410                     });
45411                     editorcore.syncValue();
45412                 },
45413                 tabIndex:-1
45414             });
45415             
45416             cmenu.menu.items.push({
45417                 actiontype : 'all',
45418                 html: 'Remove All CSS Classes',
45419                 handler: function(a,b) {
45420                     
45421                     var c = Roo.get(editorcore.doc.body);
45422                     c.select('[class]').each(function(s) {
45423                         s.dom.className = '';
45424                     });
45425                     editorcore.syncValue();
45426                 },
45427                 tabIndex:-1
45428             });
45429             
45430              cmenu.menu.items.push({
45431                 actiontype : 'tidy',
45432                 html: 'Tidy HTML Source',
45433                 handler: function(a,b) {
45434                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45435                     editorcore.syncValue();
45436                 },
45437                 tabIndex:-1
45438             });
45439             
45440             
45441             tb.add(cmenu);
45442         }
45443          
45444         if (!this.disable.specialElements) {
45445             var semenu = {
45446                 text: "Other;",
45447                 cls: 'x-edit-none',
45448                 menu : {
45449                     items : []
45450                 }
45451             };
45452             for (var i =0; i < this.specialElements.length; i++) {
45453                 semenu.menu.items.push(
45454                     Roo.apply({ 
45455                         handler: function(a,b) {
45456                             editor.insertAtCursor(this.ihtml);
45457                         }
45458                     }, this.specialElements[i])
45459                 );
45460                     
45461             }
45462             
45463             tb.add(semenu);
45464             
45465             
45466         }
45467          
45468         
45469         if (this.btns) {
45470             for(var i =0; i< this.btns.length;i++) {
45471                 var b = Roo.factory(this.btns[i],Roo.form);
45472                 b.cls =  'x-edit-none';
45473                 
45474                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45475                     b.cls += ' x-init-enable';
45476                 }
45477                 
45478                 b.scope = editorcore;
45479                 tb.add(b);
45480             }
45481         
45482         }
45483         
45484         
45485         
45486         // disable everything...
45487         
45488         this.tb.items.each(function(item){
45489             
45490            if(
45491                 item.id != editorcore.frameId+ '-sourceedit' && 
45492                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45493             ){
45494                 
45495                 item.disable();
45496             }
45497         });
45498         this.rendered = true;
45499         
45500         // the all the btns;
45501         editor.on('editorevent', this.updateToolbar, this);
45502         // other toolbars need to implement this..
45503         //editor.on('editmodechange', this.updateToolbar, this);
45504     },
45505     
45506     
45507     relayBtnCmd : function(btn) {
45508         this.editorcore.relayCmd(btn.cmd);
45509     },
45510     // private used internally
45511     createLink : function(){
45512         Roo.log("create link?");
45513         var url = prompt(this.createLinkText, this.defaultLinkValue);
45514         if(url && url != 'http:/'+'/'){
45515             this.editorcore.relayCmd('createlink', url);
45516         }
45517     },
45518
45519     
45520     /**
45521      * Protected method that will not generally be called directly. It triggers
45522      * a toolbar update by reading the markup state of the current selection in the editor.
45523      */
45524     updateToolbar: function(){
45525
45526         if(!this.editorcore.activated){
45527             this.editor.onFirstFocus();
45528             return;
45529         }
45530
45531         var btns = this.tb.items.map, 
45532             doc = this.editorcore.doc,
45533             frameId = this.editorcore.frameId;
45534
45535         if(!this.disable.font && !Roo.isSafari){
45536             /*
45537             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45538             if(name != this.fontSelect.dom.value){
45539                 this.fontSelect.dom.value = name;
45540             }
45541             */
45542         }
45543         if(!this.disable.format){
45544             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45545             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45546             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45547             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45548         }
45549         if(!this.disable.alignments){
45550             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45551             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45552             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45553         }
45554         if(!Roo.isSafari && !this.disable.lists){
45555             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45556             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45557         }
45558         
45559         var ans = this.editorcore.getAllAncestors();
45560         if (this.formatCombo) {
45561             
45562             
45563             var store = this.formatCombo.store;
45564             this.formatCombo.setValue("");
45565             for (var i =0; i < ans.length;i++) {
45566                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45567                     // select it..
45568                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45569                     break;
45570                 }
45571             }
45572         }
45573         
45574         
45575         
45576         // hides menus... - so this cant be on a menu...
45577         Roo.menu.MenuMgr.hideAll();
45578
45579         //this.editorsyncValue();
45580     },
45581    
45582     
45583     createFontOptions : function(){
45584         var buf = [], fs = this.fontFamilies, ff, lc;
45585         
45586         
45587         
45588         for(var i = 0, len = fs.length; i< len; i++){
45589             ff = fs[i];
45590             lc = ff.toLowerCase();
45591             buf.push(
45592                 '<option value="',lc,'" style="font-family:',ff,';"',
45593                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45594                     ff,
45595                 '</option>'
45596             );
45597         }
45598         return buf.join('');
45599     },
45600     
45601     toggleSourceEdit : function(sourceEditMode){
45602         
45603         Roo.log("toolbar toogle");
45604         if(sourceEditMode === undefined){
45605             sourceEditMode = !this.sourceEditMode;
45606         }
45607         this.sourceEditMode = sourceEditMode === true;
45608         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45609         // just toggle the button?
45610         if(btn.pressed !== this.sourceEditMode){
45611             btn.toggle(this.sourceEditMode);
45612             return;
45613         }
45614         
45615         if(sourceEditMode){
45616             Roo.log("disabling buttons");
45617             this.tb.items.each(function(item){
45618                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45619                     item.disable();
45620                 }
45621             });
45622           
45623         }else{
45624             Roo.log("enabling buttons");
45625             if(this.editorcore.initialized){
45626                 this.tb.items.each(function(item){
45627                     item.enable();
45628                 });
45629             }
45630             
45631         }
45632         Roo.log("calling toggole on editor");
45633         // tell the editor that it's been pressed..
45634         this.editor.toggleSourceEdit(sourceEditMode);
45635        
45636     },
45637      /**
45638      * Object collection of toolbar tooltips for the buttons in the editor. The key
45639      * is the command id associated with that button and the value is a valid QuickTips object.
45640      * For example:
45641 <pre><code>
45642 {
45643     bold : {
45644         title: 'Bold (Ctrl+B)',
45645         text: 'Make the selected text bold.',
45646         cls: 'x-html-editor-tip'
45647     },
45648     italic : {
45649         title: 'Italic (Ctrl+I)',
45650         text: 'Make the selected text italic.',
45651         cls: 'x-html-editor-tip'
45652     },
45653     ...
45654 </code></pre>
45655     * @type Object
45656      */
45657     buttonTips : {
45658         bold : {
45659             title: 'Bold (Ctrl+B)',
45660             text: 'Make the selected text bold.',
45661             cls: 'x-html-editor-tip'
45662         },
45663         italic : {
45664             title: 'Italic (Ctrl+I)',
45665             text: 'Make the selected text italic.',
45666             cls: 'x-html-editor-tip'
45667         },
45668         underline : {
45669             title: 'Underline (Ctrl+U)',
45670             text: 'Underline the selected text.',
45671             cls: 'x-html-editor-tip'
45672         },
45673         strikethrough : {
45674             title: 'Strikethrough',
45675             text: 'Strikethrough the selected text.',
45676             cls: 'x-html-editor-tip'
45677         },
45678         increasefontsize : {
45679             title: 'Grow Text',
45680             text: 'Increase the font size.',
45681             cls: 'x-html-editor-tip'
45682         },
45683         decreasefontsize : {
45684             title: 'Shrink Text',
45685             text: 'Decrease the font size.',
45686             cls: 'x-html-editor-tip'
45687         },
45688         backcolor : {
45689             title: 'Text Highlight Color',
45690             text: 'Change the background color of the selected text.',
45691             cls: 'x-html-editor-tip'
45692         },
45693         forecolor : {
45694             title: 'Font Color',
45695             text: 'Change the color of the selected text.',
45696             cls: 'x-html-editor-tip'
45697         },
45698         justifyleft : {
45699             title: 'Align Text Left',
45700             text: 'Align text to the left.',
45701             cls: 'x-html-editor-tip'
45702         },
45703         justifycenter : {
45704             title: 'Center Text',
45705             text: 'Center text in the editor.',
45706             cls: 'x-html-editor-tip'
45707         },
45708         justifyright : {
45709             title: 'Align Text Right',
45710             text: 'Align text to the right.',
45711             cls: 'x-html-editor-tip'
45712         },
45713         insertunorderedlist : {
45714             title: 'Bullet List',
45715             text: 'Start a bulleted list.',
45716             cls: 'x-html-editor-tip'
45717         },
45718         insertorderedlist : {
45719             title: 'Numbered List',
45720             text: 'Start a numbered list.',
45721             cls: 'x-html-editor-tip'
45722         },
45723         createlink : {
45724             title: 'Hyperlink',
45725             text: 'Make the selected text a hyperlink.',
45726             cls: 'x-html-editor-tip'
45727         },
45728         sourceedit : {
45729             title: 'Source Edit',
45730             text: 'Switch to source editing mode.',
45731             cls: 'x-html-editor-tip'
45732         }
45733     },
45734     // private
45735     onDestroy : function(){
45736         if(this.rendered){
45737             
45738             this.tb.items.each(function(item){
45739                 if(item.menu){
45740                     item.menu.removeAll();
45741                     if(item.menu.el){
45742                         item.menu.el.destroy();
45743                     }
45744                 }
45745                 item.destroy();
45746             });
45747              
45748         }
45749     },
45750     onFirstFocus: function() {
45751         this.tb.items.each(function(item){
45752            item.enable();
45753         });
45754     }
45755 });
45756
45757
45758
45759
45760 // <script type="text/javascript">
45761 /*
45762  * Based on
45763  * Ext JS Library 1.1.1
45764  * Copyright(c) 2006-2007, Ext JS, LLC.
45765  *  
45766  
45767  */
45768
45769  
45770 /**
45771  * @class Roo.form.HtmlEditor.ToolbarContext
45772  * Context Toolbar
45773  * 
45774  * Usage:
45775  *
45776  new Roo.form.HtmlEditor({
45777     ....
45778     toolbars : [
45779         { xtype: 'ToolbarStandard', styles : {} }
45780         { xtype: 'ToolbarContext', disable : {} }
45781     ]
45782 })
45783
45784      
45785  * 
45786  * @config : {Object} disable List of elements to disable.. (not done yet.)
45787  * @config : {Object} styles  Map of styles available.
45788  * 
45789  */
45790
45791 Roo.form.HtmlEditor.ToolbarContext = function(config)
45792 {
45793     
45794     Roo.apply(this, config);
45795     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45796     // dont call parent... till later.
45797     this.styles = this.styles || {};
45798 }
45799
45800  
45801
45802 Roo.form.HtmlEditor.ToolbarContext.types = {
45803     'IMG' : {
45804         width : {
45805             title: "Width",
45806             width: 40
45807         },
45808         height:  {
45809             title: "Height",
45810             width: 40
45811         },
45812         align: {
45813             title: "Align",
45814             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45815             width : 80
45816             
45817         },
45818         border: {
45819             title: "Border",
45820             width: 40
45821         },
45822         alt: {
45823             title: "Alt",
45824             width: 120
45825         },
45826         src : {
45827             title: "Src",
45828             width: 220
45829         }
45830         
45831     },
45832     'A' : {
45833         name : {
45834             title: "Name",
45835             width: 50
45836         },
45837         target:  {
45838             title: "Target",
45839             width: 120
45840         },
45841         href:  {
45842             title: "Href",
45843             width: 220
45844         } // border?
45845         
45846     },
45847     'TABLE' : {
45848         rows : {
45849             title: "Rows",
45850             width: 20
45851         },
45852         cols : {
45853             title: "Cols",
45854             width: 20
45855         },
45856         width : {
45857             title: "Width",
45858             width: 40
45859         },
45860         height : {
45861             title: "Height",
45862             width: 40
45863         },
45864         border : {
45865             title: "Border",
45866             width: 20
45867         }
45868     },
45869     'TD' : {
45870         width : {
45871             title: "Width",
45872             width: 40
45873         },
45874         height : {
45875             title: "Height",
45876             width: 40
45877         },   
45878         align: {
45879             title: "Align",
45880             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45881             width: 80
45882         },
45883         valign: {
45884             title: "Valign",
45885             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45886             width: 80
45887         },
45888         colspan: {
45889             title: "Colspan",
45890             width: 20
45891             
45892         },
45893          'font-family'  : {
45894             title : "Font",
45895             style : 'fontFamily',
45896             displayField: 'display',
45897             optname : 'font-family',
45898             width: 140
45899         }
45900     },
45901     'INPUT' : {
45902         name : {
45903             title: "name",
45904             width: 120
45905         },
45906         value : {
45907             title: "Value",
45908             width: 120
45909         },
45910         width : {
45911             title: "Width",
45912             width: 40
45913         }
45914     },
45915     'LABEL' : {
45916         'for' : {
45917             title: "For",
45918             width: 120
45919         }
45920     },
45921     'TEXTAREA' : {
45922           name : {
45923             title: "name",
45924             width: 120
45925         },
45926         rows : {
45927             title: "Rows",
45928             width: 20
45929         },
45930         cols : {
45931             title: "Cols",
45932             width: 20
45933         }
45934     },
45935     'SELECT' : {
45936         name : {
45937             title: "name",
45938             width: 120
45939         },
45940         selectoptions : {
45941             title: "Options",
45942             width: 200
45943         }
45944     },
45945     
45946     // should we really allow this??
45947     // should this just be 
45948     'BODY' : {
45949         title : {
45950             title: "Title",
45951             width: 200,
45952             disabled : true
45953         }
45954     },
45955     'SPAN' : {
45956         'font-family'  : {
45957             title : "Font",
45958             style : 'fontFamily',
45959             displayField: 'display',
45960             optname : 'font-family',
45961             width: 140
45962         }
45963     },
45964     'DIV' : {
45965         'font-family'  : {
45966             title : "Font",
45967             style : 'fontFamily',
45968             displayField: 'display',
45969             optname : 'font-family',
45970             width: 140
45971         }
45972     },
45973      'P' : {
45974         'font-family'  : {
45975             title : "Font",
45976             style : 'fontFamily',
45977             displayField: 'display',
45978             optname : 'font-family',
45979             width: 140
45980         }
45981     },
45982     
45983     '*' : {
45984         // empty..
45985     }
45986
45987 };
45988
45989 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45990 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45991
45992 Roo.form.HtmlEditor.ToolbarContext.options = {
45993         'font-family'  : [ 
45994                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45995                 [ 'Courier New', 'Courier New'],
45996                 [ 'Tahoma', 'Tahoma'],
45997                 [ 'Times New Roman,serif', 'Times'],
45998                 [ 'Verdana','Verdana' ]
45999         ]
46000 };
46001
46002 // fixme - these need to be configurable..
46003  
46004
46005 //Roo.form.HtmlEditor.ToolbarContext.types
46006
46007
46008 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46009     
46010     tb: false,
46011     
46012     rendered: false,
46013     
46014     editor : false,
46015     editorcore : false,
46016     /**
46017      * @cfg {Object} disable  List of toolbar elements to disable
46018          
46019      */
46020     disable : false,
46021     /**
46022      * @cfg {Object} styles List of styles 
46023      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46024      *
46025      * These must be defined in the page, so they get rendered correctly..
46026      * .headline { }
46027      * TD.underline { }
46028      * 
46029      */
46030     styles : false,
46031     
46032     options: false,
46033     
46034     toolbars : false,
46035     
46036     init : function(editor)
46037     {
46038         this.editor = editor;
46039         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46040         var editorcore = this.editorcore;
46041         
46042         var fid = editorcore.frameId;
46043         var etb = this;
46044         function btn(id, toggle, handler){
46045             var xid = fid + '-'+ id ;
46046             return {
46047                 id : xid,
46048                 cmd : id,
46049                 cls : 'x-btn-icon x-edit-'+id,
46050                 enableToggle:toggle !== false,
46051                 scope: editorcore, // was editor...
46052                 handler:handler||editorcore.relayBtnCmd,
46053                 clickEvent:'mousedown',
46054                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46055                 tabIndex:-1
46056             };
46057         }
46058         // create a new element.
46059         var wdiv = editor.wrap.createChild({
46060                 tag: 'div'
46061             }, editor.wrap.dom.firstChild.nextSibling, true);
46062         
46063         // can we do this more than once??
46064         
46065          // stop form submits
46066       
46067  
46068         // disable everything...
46069         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46070         this.toolbars = {};
46071            
46072         for (var i in  ty) {
46073           
46074             this.toolbars[i] = this.buildToolbar(ty[i],i);
46075         }
46076         this.tb = this.toolbars.BODY;
46077         this.tb.el.show();
46078         this.buildFooter();
46079         this.footer.show();
46080         editor.on('hide', function( ) { this.footer.hide() }, this);
46081         editor.on('show', function( ) { this.footer.show() }, this);
46082         
46083          
46084         this.rendered = true;
46085         
46086         // the all the btns;
46087         editor.on('editorevent', this.updateToolbar, this);
46088         // other toolbars need to implement this..
46089         //editor.on('editmodechange', this.updateToolbar, this);
46090     },
46091     
46092     
46093     
46094     /**
46095      * Protected method that will not generally be called directly. It triggers
46096      * a toolbar update by reading the markup state of the current selection in the editor.
46097      *
46098      * Note you can force an update by calling on('editorevent', scope, false)
46099      */
46100     updateToolbar: function(editor,ev,sel){
46101
46102         //Roo.log(ev);
46103         // capture mouse up - this is handy for selecting images..
46104         // perhaps should go somewhere else...
46105         if(!this.editorcore.activated){
46106              this.editor.onFirstFocus();
46107             return;
46108         }
46109         
46110         
46111         
46112         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46113         // selectNode - might want to handle IE?
46114         if (ev &&
46115             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46116             ev.target && ev.target.tagName == 'IMG') {
46117             // they have click on an image...
46118             // let's see if we can change the selection...
46119             sel = ev.target;
46120          
46121               var nodeRange = sel.ownerDocument.createRange();
46122             try {
46123                 nodeRange.selectNode(sel);
46124             } catch (e) {
46125                 nodeRange.selectNodeContents(sel);
46126             }
46127             //nodeRange.collapse(true);
46128             var s = this.editorcore.win.getSelection();
46129             s.removeAllRanges();
46130             s.addRange(nodeRange);
46131         }  
46132         
46133       
46134         var updateFooter = sel ? false : true;
46135         
46136         
46137         var ans = this.editorcore.getAllAncestors();
46138         
46139         // pick
46140         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46141         
46142         if (!sel) { 
46143             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46144             sel = sel ? sel : this.editorcore.doc.body;
46145             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46146             
46147         }
46148         // pick a menu that exists..
46149         var tn = sel.tagName.toUpperCase();
46150         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46151         
46152         tn = sel.tagName.toUpperCase();
46153         
46154         var lastSel = this.tb.selectedNode;
46155         
46156         this.tb.selectedNode = sel;
46157         
46158         // if current menu does not match..
46159         
46160         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46161                 
46162             this.tb.el.hide();
46163             ///console.log("show: " + tn);
46164             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46165             this.tb.el.show();
46166             // update name
46167             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46168             
46169             
46170             // update attributes
46171             if (this.tb.fields) {
46172                 this.tb.fields.each(function(e) {
46173                     if (e.stylename) {
46174                         e.setValue(sel.style[e.stylename]);
46175                         return;
46176                     } 
46177                    e.setValue(sel.getAttribute(e.attrname));
46178                 });
46179             }
46180             
46181             var hasStyles = false;
46182             for(var i in this.styles) {
46183                 hasStyles = true;
46184                 break;
46185             }
46186             
46187             // update styles
46188             if (hasStyles) { 
46189                 var st = this.tb.fields.item(0);
46190                 
46191                 st.store.removeAll();
46192                
46193                 
46194                 var cn = sel.className.split(/\s+/);
46195                 
46196                 var avs = [];
46197                 if (this.styles['*']) {
46198                     
46199                     Roo.each(this.styles['*'], function(v) {
46200                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46201                     });
46202                 }
46203                 if (this.styles[tn]) { 
46204                     Roo.each(this.styles[tn], function(v) {
46205                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46206                     });
46207                 }
46208                 
46209                 st.store.loadData(avs);
46210                 st.collapse();
46211                 st.setValue(cn);
46212             }
46213             // flag our selected Node.
46214             this.tb.selectedNode = sel;
46215            
46216            
46217             Roo.menu.MenuMgr.hideAll();
46218
46219         }
46220         
46221         if (!updateFooter) {
46222             //this.footDisp.dom.innerHTML = ''; 
46223             return;
46224         }
46225         // update the footer
46226         //
46227         var html = '';
46228         
46229         this.footerEls = ans.reverse();
46230         Roo.each(this.footerEls, function(a,i) {
46231             if (!a) { return; }
46232             html += html.length ? ' &gt; '  :  '';
46233             
46234             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46235             
46236         });
46237        
46238         // 
46239         var sz = this.footDisp.up('td').getSize();
46240         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46241         this.footDisp.dom.style.marginLeft = '5px';
46242         
46243         this.footDisp.dom.style.overflow = 'hidden';
46244         
46245         this.footDisp.dom.innerHTML = html;
46246             
46247         //this.editorsyncValue();
46248     },
46249      
46250     
46251    
46252        
46253     // private
46254     onDestroy : function(){
46255         if(this.rendered){
46256             
46257             this.tb.items.each(function(item){
46258                 if(item.menu){
46259                     item.menu.removeAll();
46260                     if(item.menu.el){
46261                         item.menu.el.destroy();
46262                     }
46263                 }
46264                 item.destroy();
46265             });
46266              
46267         }
46268     },
46269     onFirstFocus: function() {
46270         // need to do this for all the toolbars..
46271         this.tb.items.each(function(item){
46272            item.enable();
46273         });
46274     },
46275     buildToolbar: function(tlist, nm)
46276     {
46277         var editor = this.editor;
46278         var editorcore = this.editorcore;
46279          // create a new element.
46280         var wdiv = editor.wrap.createChild({
46281                 tag: 'div'
46282             }, editor.wrap.dom.firstChild.nextSibling, true);
46283         
46284        
46285         var tb = new Roo.Toolbar(wdiv);
46286         // add the name..
46287         
46288         tb.add(nm+ ":&nbsp;");
46289         
46290         var styles = [];
46291         for(var i in this.styles) {
46292             styles.push(i);
46293         }
46294         
46295         // styles...
46296         if (styles && styles.length) {
46297             
46298             // this needs a multi-select checkbox...
46299             tb.addField( new Roo.form.ComboBox({
46300                 store: new Roo.data.SimpleStore({
46301                     id : 'val',
46302                     fields: ['val', 'selected'],
46303                     data : [] 
46304                 }),
46305                 name : '-roo-edit-className',
46306                 attrname : 'className',
46307                 displayField: 'val',
46308                 typeAhead: false,
46309                 mode: 'local',
46310                 editable : false,
46311                 triggerAction: 'all',
46312                 emptyText:'Select Style',
46313                 selectOnFocus:true,
46314                 width: 130,
46315                 listeners : {
46316                     'select': function(c, r, i) {
46317                         // initial support only for on class per el..
46318                         tb.selectedNode.className =  r ? r.get('val') : '';
46319                         editorcore.syncValue();
46320                     }
46321                 }
46322     
46323             }));
46324         }
46325         
46326         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46327         var tbops = tbc.options;
46328         
46329         for (var i in tlist) {
46330             
46331             var item = tlist[i];
46332             tb.add(item.title + ":&nbsp;");
46333             
46334             
46335             //optname == used so you can configure the options available..
46336             var opts = item.opts ? item.opts : false;
46337             if (item.optname) {
46338                 opts = tbops[item.optname];
46339            
46340             }
46341             
46342             if (opts) {
46343                 // opts == pulldown..
46344                 tb.addField( new Roo.form.ComboBox({
46345                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46346                         id : 'val',
46347                         fields: ['val', 'display'],
46348                         data : opts  
46349                     }),
46350                     name : '-roo-edit-' + i,
46351                     attrname : i,
46352                     stylename : item.style ? item.style : false,
46353                     displayField: item.displayField ? item.displayField : 'val',
46354                     valueField :  'val',
46355                     typeAhead: false,
46356                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46357                     editable : false,
46358                     triggerAction: 'all',
46359                     emptyText:'Select',
46360                     selectOnFocus:true,
46361                     width: item.width ? item.width  : 130,
46362                     listeners : {
46363                         'select': function(c, r, i) {
46364                             if (c.stylename) {
46365                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46366                                 return;
46367                             }
46368                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46369                         }
46370                     }
46371
46372                 }));
46373                 continue;
46374                     
46375                  
46376                 
46377                 tb.addField( new Roo.form.TextField({
46378                     name: i,
46379                     width: 100,
46380                     //allowBlank:false,
46381                     value: ''
46382                 }));
46383                 continue;
46384             }
46385             tb.addField( new Roo.form.TextField({
46386                 name: '-roo-edit-' + i,
46387                 attrname : i,
46388                 
46389                 width: item.width,
46390                 //allowBlank:true,
46391                 value: '',
46392                 listeners: {
46393                     'change' : function(f, nv, ov) {
46394                         tb.selectedNode.setAttribute(f.attrname, nv);
46395                         editorcore.syncValue();
46396                     }
46397                 }
46398             }));
46399              
46400         }
46401         
46402         var _this = this;
46403         
46404         if(nm == 'BODY'){
46405             tb.addSeparator();
46406         
46407             tb.addButton( {
46408                 text: 'Stylesheets',
46409
46410                 listeners : {
46411                     click : function ()
46412                     {
46413                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46414                     }
46415                 }
46416             });
46417         }
46418         
46419         tb.addFill();
46420         tb.addButton( {
46421             text: 'Remove Tag',
46422     
46423             listeners : {
46424                 click : function ()
46425                 {
46426                     // remove
46427                     // undo does not work.
46428                      
46429                     var sn = tb.selectedNode;
46430                     
46431                     var pn = sn.parentNode;
46432                     
46433                     var stn =  sn.childNodes[0];
46434                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46435                     while (sn.childNodes.length) {
46436                         var node = sn.childNodes[0];
46437                         sn.removeChild(node);
46438                         //Roo.log(node);
46439                         pn.insertBefore(node, sn);
46440                         
46441                     }
46442                     pn.removeChild(sn);
46443                     var range = editorcore.createRange();
46444         
46445                     range.setStart(stn,0);
46446                     range.setEnd(en,0); //????
46447                     //range.selectNode(sel);
46448                     
46449                     
46450                     var selection = editorcore.getSelection();
46451                     selection.removeAllRanges();
46452                     selection.addRange(range);
46453                     
46454                     
46455                     
46456                     //_this.updateToolbar(null, null, pn);
46457                     _this.updateToolbar(null, null, null);
46458                     _this.footDisp.dom.innerHTML = ''; 
46459                 }
46460             }
46461             
46462                     
46463                 
46464             
46465         });
46466         
46467         
46468         tb.el.on('click', function(e){
46469             e.preventDefault(); // what does this do?
46470         });
46471         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46472         tb.el.hide();
46473         tb.name = nm;
46474         // dont need to disable them... as they will get hidden
46475         return tb;
46476          
46477         
46478     },
46479     buildFooter : function()
46480     {
46481         
46482         var fel = this.editor.wrap.createChild();
46483         this.footer = new Roo.Toolbar(fel);
46484         // toolbar has scrolly on left / right?
46485         var footDisp= new Roo.Toolbar.Fill();
46486         var _t = this;
46487         this.footer.add(
46488             {
46489                 text : '&lt;',
46490                 xtype: 'Button',
46491                 handler : function() {
46492                     _t.footDisp.scrollTo('left',0,true)
46493                 }
46494             }
46495         );
46496         this.footer.add( footDisp );
46497         this.footer.add( 
46498             {
46499                 text : '&gt;',
46500                 xtype: 'Button',
46501                 handler : function() {
46502                     // no animation..
46503                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46504                 }
46505             }
46506         );
46507         var fel = Roo.get(footDisp.el);
46508         fel.addClass('x-editor-context');
46509         this.footDispWrap = fel; 
46510         this.footDispWrap.overflow  = 'hidden';
46511         
46512         this.footDisp = fel.createChild();
46513         this.footDispWrap.on('click', this.onContextClick, this)
46514         
46515         
46516     },
46517     onContextClick : function (ev,dom)
46518     {
46519         ev.preventDefault();
46520         var  cn = dom.className;
46521         //Roo.log(cn);
46522         if (!cn.match(/x-ed-loc-/)) {
46523             return;
46524         }
46525         var n = cn.split('-').pop();
46526         var ans = this.footerEls;
46527         var sel = ans[n];
46528         
46529          // pick
46530         var range = this.editorcore.createRange();
46531         
46532         range.selectNodeContents(sel);
46533         //range.selectNode(sel);
46534         
46535         
46536         var selection = this.editorcore.getSelection();
46537         selection.removeAllRanges();
46538         selection.addRange(range);
46539         
46540         
46541         
46542         this.updateToolbar(null, null, sel);
46543         
46544         
46545     }
46546     
46547     
46548     
46549     
46550     
46551 });
46552
46553
46554
46555
46556
46557 /*
46558  * Based on:
46559  * Ext JS Library 1.1.1
46560  * Copyright(c) 2006-2007, Ext JS, LLC.
46561  *
46562  * Originally Released Under LGPL - original licence link has changed is not relivant.
46563  *
46564  * Fork - LGPL
46565  * <script type="text/javascript">
46566  */
46567  
46568 /**
46569  * @class Roo.form.BasicForm
46570  * @extends Roo.util.Observable
46571  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46572  * @constructor
46573  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46574  * @param {Object} config Configuration options
46575  */
46576 Roo.form.BasicForm = function(el, config){
46577     this.allItems = [];
46578     this.childForms = [];
46579     Roo.apply(this, config);
46580     /*
46581      * The Roo.form.Field items in this form.
46582      * @type MixedCollection
46583      */
46584      
46585      
46586     this.items = new Roo.util.MixedCollection(false, function(o){
46587         return o.id || (o.id = Roo.id());
46588     });
46589     this.addEvents({
46590         /**
46591          * @event beforeaction
46592          * Fires before any action is performed. Return false to cancel the action.
46593          * @param {Form} this
46594          * @param {Action} action The action to be performed
46595          */
46596         beforeaction: true,
46597         /**
46598          * @event actionfailed
46599          * Fires when an action fails.
46600          * @param {Form} this
46601          * @param {Action} action The action that failed
46602          */
46603         actionfailed : true,
46604         /**
46605          * @event actioncomplete
46606          * Fires when an action is completed.
46607          * @param {Form} this
46608          * @param {Action} action The action that completed
46609          */
46610         actioncomplete : true
46611     });
46612     if(el){
46613         this.initEl(el);
46614     }
46615     Roo.form.BasicForm.superclass.constructor.call(this);
46616 };
46617
46618 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46619     /**
46620      * @cfg {String} method
46621      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46622      */
46623     /**
46624      * @cfg {DataReader} reader
46625      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46626      * This is optional as there is built-in support for processing JSON.
46627      */
46628     /**
46629      * @cfg {DataReader} errorReader
46630      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46631      * This is completely optional as there is built-in support for processing JSON.
46632      */
46633     /**
46634      * @cfg {String} url
46635      * The URL to use for form actions if one isn't supplied in the action options.
46636      */
46637     /**
46638      * @cfg {Boolean} fileUpload
46639      * Set to true if this form is a file upload.
46640      */
46641      
46642     /**
46643      * @cfg {Object} baseParams
46644      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46645      */
46646      /**
46647      
46648     /**
46649      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46650      */
46651     timeout: 30,
46652
46653     // private
46654     activeAction : null,
46655
46656     /**
46657      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46658      * or setValues() data instead of when the form was first created.
46659      */
46660     trackResetOnLoad : false,
46661     
46662     
46663     /**
46664      * childForms - used for multi-tab forms
46665      * @type {Array}
46666      */
46667     childForms : false,
46668     
46669     /**
46670      * allItems - full list of fields.
46671      * @type {Array}
46672      */
46673     allItems : false,
46674     
46675     /**
46676      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46677      * element by passing it or its id or mask the form itself by passing in true.
46678      * @type Mixed
46679      */
46680     waitMsgTarget : false,
46681
46682     // private
46683     initEl : function(el){
46684         this.el = Roo.get(el);
46685         this.id = this.el.id || Roo.id();
46686         this.el.on('submit', this.onSubmit, this);
46687         this.el.addClass('x-form');
46688     },
46689
46690     // private
46691     onSubmit : function(e){
46692         e.stopEvent();
46693     },
46694
46695     /**
46696      * Returns true if client-side validation on the form is successful.
46697      * @return Boolean
46698      */
46699     isValid : function(){
46700         var valid = true;
46701         this.items.each(function(f){
46702            if(!f.validate()){
46703                valid = false;
46704            }
46705         });
46706         return valid;
46707     },
46708
46709     /**
46710      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46711      * @return Boolean
46712      */
46713     isDirty : function(){
46714         var dirty = false;
46715         this.items.each(function(f){
46716            if(f.isDirty()){
46717                dirty = true;
46718                return false;
46719            }
46720         });
46721         return dirty;
46722     },
46723     
46724     /**
46725      * Returns true if any fields in this form have changed since their original load. (New version)
46726      * @return Boolean
46727      */
46728     
46729     hasChanged : function()
46730     {
46731         var dirty = false;
46732         this.items.each(function(f){
46733            if(f.hasChanged()){
46734                dirty = true;
46735                return false;
46736            }
46737         });
46738         return dirty;
46739         
46740     },
46741     /**
46742      * Resets all hasChanged to 'false' -
46743      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46744      * So hasChanged storage is only to be used for this purpose
46745      * @return Boolean
46746      */
46747     resetHasChanged : function()
46748     {
46749         this.items.each(function(f){
46750            f.resetHasChanged();
46751         });
46752         
46753     },
46754     
46755     
46756     /**
46757      * Performs a predefined action (submit or load) or custom actions you define on this form.
46758      * @param {String} actionName The name of the action type
46759      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46760      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46761      * accept other config options):
46762      * <pre>
46763 Property          Type             Description
46764 ----------------  ---------------  ----------------------------------------------------------------------------------
46765 url               String           The url for the action (defaults to the form's url)
46766 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46767 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46768 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46769                                    validate the form on the client (defaults to false)
46770      * </pre>
46771      * @return {BasicForm} this
46772      */
46773     doAction : function(action, options){
46774         if(typeof action == 'string'){
46775             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46776         }
46777         if(this.fireEvent('beforeaction', this, action) !== false){
46778             this.beforeAction(action);
46779             action.run.defer(100, action);
46780         }
46781         return this;
46782     },
46783
46784     /**
46785      * Shortcut to do a submit action.
46786      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46787      * @return {BasicForm} this
46788      */
46789     submit : function(options){
46790         this.doAction('submit', options);
46791         return this;
46792     },
46793
46794     /**
46795      * Shortcut to do a load action.
46796      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46797      * @return {BasicForm} this
46798      */
46799     load : function(options){
46800         this.doAction('load', options);
46801         return this;
46802     },
46803
46804     /**
46805      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46806      * @param {Record} record The record to edit
46807      * @return {BasicForm} this
46808      */
46809     updateRecord : function(record){
46810         record.beginEdit();
46811         var fs = record.fields;
46812         fs.each(function(f){
46813             var field = this.findField(f.name);
46814             if(field){
46815                 record.set(f.name, field.getValue());
46816             }
46817         }, this);
46818         record.endEdit();
46819         return this;
46820     },
46821
46822     /**
46823      * Loads an Roo.data.Record into this form.
46824      * @param {Record} record The record to load
46825      * @return {BasicForm} this
46826      */
46827     loadRecord : function(record){
46828         this.setValues(record.data);
46829         return this;
46830     },
46831
46832     // private
46833     beforeAction : function(action){
46834         var o = action.options;
46835         
46836        
46837         if(this.waitMsgTarget === true){
46838             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46839         }else if(this.waitMsgTarget){
46840             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46841             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46842         }else {
46843             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46844         }
46845          
46846     },
46847
46848     // private
46849     afterAction : function(action, success){
46850         this.activeAction = null;
46851         var o = action.options;
46852         
46853         if(this.waitMsgTarget === true){
46854             this.el.unmask();
46855         }else if(this.waitMsgTarget){
46856             this.waitMsgTarget.unmask();
46857         }else{
46858             Roo.MessageBox.updateProgress(1);
46859             Roo.MessageBox.hide();
46860         }
46861          
46862         if(success){
46863             if(o.reset){
46864                 this.reset();
46865             }
46866             Roo.callback(o.success, o.scope, [this, action]);
46867             this.fireEvent('actioncomplete', this, action);
46868             
46869         }else{
46870             
46871             // failure condition..
46872             // we have a scenario where updates need confirming.
46873             // eg. if a locking scenario exists..
46874             // we look for { errors : { needs_confirm : true }} in the response.
46875             if (
46876                 (typeof(action.result) != 'undefined')  &&
46877                 (typeof(action.result.errors) != 'undefined')  &&
46878                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46879            ){
46880                 var _t = this;
46881                 Roo.MessageBox.confirm(
46882                     "Change requires confirmation",
46883                     action.result.errorMsg,
46884                     function(r) {
46885                         if (r != 'yes') {
46886                             return;
46887                         }
46888                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46889                     }
46890                     
46891                 );
46892                 
46893                 
46894                 
46895                 return;
46896             }
46897             
46898             Roo.callback(o.failure, o.scope, [this, action]);
46899             // show an error message if no failed handler is set..
46900             if (!this.hasListener('actionfailed')) {
46901                 Roo.MessageBox.alert("Error",
46902                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46903                         action.result.errorMsg :
46904                         "Saving Failed, please check your entries or try again"
46905                 );
46906             }
46907             
46908             this.fireEvent('actionfailed', this, action);
46909         }
46910         
46911     },
46912
46913     /**
46914      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46915      * @param {String} id The value to search for
46916      * @return Field
46917      */
46918     findField : function(id){
46919         var field = this.items.get(id);
46920         if(!field){
46921             this.items.each(function(f){
46922                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46923                     field = f;
46924                     return false;
46925                 }
46926             });
46927         }
46928         return field || null;
46929     },
46930
46931     /**
46932      * Add a secondary form to this one, 
46933      * Used to provide tabbed forms. One form is primary, with hidden values 
46934      * which mirror the elements from the other forms.
46935      * 
46936      * @param {Roo.form.Form} form to add.
46937      * 
46938      */
46939     addForm : function(form)
46940     {
46941        
46942         if (this.childForms.indexOf(form) > -1) {
46943             // already added..
46944             return;
46945         }
46946         this.childForms.push(form);
46947         var n = '';
46948         Roo.each(form.allItems, function (fe) {
46949             
46950             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46951             if (this.findField(n)) { // already added..
46952                 return;
46953             }
46954             var add = new Roo.form.Hidden({
46955                 name : n
46956             });
46957             add.render(this.el);
46958             
46959             this.add( add );
46960         }, this);
46961         
46962     },
46963     /**
46964      * Mark fields in this form invalid in bulk.
46965      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46966      * @return {BasicForm} this
46967      */
46968     markInvalid : function(errors){
46969         if(errors instanceof Array){
46970             for(var i = 0, len = errors.length; i < len; i++){
46971                 var fieldError = errors[i];
46972                 var f = this.findField(fieldError.id);
46973                 if(f){
46974                     f.markInvalid(fieldError.msg);
46975                 }
46976             }
46977         }else{
46978             var field, id;
46979             for(id in errors){
46980                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46981                     field.markInvalid(errors[id]);
46982                 }
46983             }
46984         }
46985         Roo.each(this.childForms || [], function (f) {
46986             f.markInvalid(errors);
46987         });
46988         
46989         return this;
46990     },
46991
46992     /**
46993      * Set values for fields in this form in bulk.
46994      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46995      * @return {BasicForm} this
46996      */
46997     setValues : function(values){
46998         if(values instanceof Array){ // array of objects
46999             for(var i = 0, len = values.length; i < len; i++){
47000                 var v = values[i];
47001                 var f = this.findField(v.id);
47002                 if(f){
47003                     f.setValue(v.value);
47004                     if(this.trackResetOnLoad){
47005                         f.originalValue = f.getValue();
47006                     }
47007                 }
47008             }
47009         }else{ // object hash
47010             var field, id;
47011             for(id in values){
47012                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47013                     
47014                     if (field.setFromData && 
47015                         field.valueField && 
47016                         field.displayField &&
47017                         // combos' with local stores can 
47018                         // be queried via setValue()
47019                         // to set their value..
47020                         (field.store && !field.store.isLocal)
47021                         ) {
47022                         // it's a combo
47023                         var sd = { };
47024                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47025                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47026                         field.setFromData(sd);
47027                         
47028                     } else {
47029                         field.setValue(values[id]);
47030                     }
47031                     
47032                     
47033                     if(this.trackResetOnLoad){
47034                         field.originalValue = field.getValue();
47035                     }
47036                 }
47037             }
47038         }
47039         this.resetHasChanged();
47040         
47041         
47042         Roo.each(this.childForms || [], function (f) {
47043             f.setValues(values);
47044             f.resetHasChanged();
47045         });
47046                 
47047         return this;
47048     },
47049
47050     /**
47051      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47052      * they are returned as an array.
47053      * @param {Boolean} asString
47054      * @return {Object}
47055      */
47056     getValues : function(asString){
47057         if (this.childForms) {
47058             // copy values from the child forms
47059             Roo.each(this.childForms, function (f) {
47060                 this.setValues(f.getValues());
47061             }, this);
47062         }
47063         
47064         
47065         
47066         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47067         if(asString === true){
47068             return fs;
47069         }
47070         return Roo.urlDecode(fs);
47071     },
47072     
47073     /**
47074      * Returns the fields in this form as an object with key/value pairs. 
47075      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47076      * @return {Object}
47077      */
47078     getFieldValues : function(with_hidden)
47079     {
47080         if (this.childForms) {
47081             // copy values from the child forms
47082             // should this call getFieldValues - probably not as we do not currently copy
47083             // hidden fields when we generate..
47084             Roo.each(this.childForms, function (f) {
47085                 this.setValues(f.getValues());
47086             }, this);
47087         }
47088         
47089         var ret = {};
47090         this.items.each(function(f){
47091             if (!f.getName()) {
47092                 return;
47093             }
47094             var v = f.getValue();
47095             if (f.inputType =='radio') {
47096                 if (typeof(ret[f.getName()]) == 'undefined') {
47097                     ret[f.getName()] = ''; // empty..
47098                 }
47099                 
47100                 if (!f.el.dom.checked) {
47101                     return;
47102                     
47103                 }
47104                 v = f.el.dom.value;
47105                 
47106             }
47107             
47108             // not sure if this supported any more..
47109             if ((typeof(v) == 'object') && f.getRawValue) {
47110                 v = f.getRawValue() ; // dates..
47111             }
47112             // combo boxes where name != hiddenName...
47113             if (f.name != f.getName()) {
47114                 ret[f.name] = f.getRawValue();
47115             }
47116             ret[f.getName()] = v;
47117         });
47118         
47119         return ret;
47120     },
47121
47122     /**
47123      * Clears all invalid messages in this form.
47124      * @return {BasicForm} this
47125      */
47126     clearInvalid : function(){
47127         this.items.each(function(f){
47128            f.clearInvalid();
47129         });
47130         
47131         Roo.each(this.childForms || [], function (f) {
47132             f.clearInvalid();
47133         });
47134         
47135         
47136         return this;
47137     },
47138
47139     /**
47140      * Resets this form.
47141      * @return {BasicForm} this
47142      */
47143     reset : function(){
47144         this.items.each(function(f){
47145             f.reset();
47146         });
47147         
47148         Roo.each(this.childForms || [], function (f) {
47149             f.reset();
47150         });
47151         this.resetHasChanged();
47152         
47153         return this;
47154     },
47155
47156     /**
47157      * Add Roo.form components to this form.
47158      * @param {Field} field1
47159      * @param {Field} field2 (optional)
47160      * @param {Field} etc (optional)
47161      * @return {BasicForm} this
47162      */
47163     add : function(){
47164         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47165         return this;
47166     },
47167
47168
47169     /**
47170      * Removes a field from the items collection (does NOT remove its markup).
47171      * @param {Field} field
47172      * @return {BasicForm} this
47173      */
47174     remove : function(field){
47175         this.items.remove(field);
47176         return this;
47177     },
47178
47179     /**
47180      * Looks at the fields in this form, checks them for an id attribute,
47181      * and calls applyTo on the existing dom element with that id.
47182      * @return {BasicForm} this
47183      */
47184     render : function(){
47185         this.items.each(function(f){
47186             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47187                 f.applyTo(f.id);
47188             }
47189         });
47190         return this;
47191     },
47192
47193     /**
47194      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47195      * @param {Object} values
47196      * @return {BasicForm} this
47197      */
47198     applyToFields : function(o){
47199         this.items.each(function(f){
47200            Roo.apply(f, o);
47201         });
47202         return this;
47203     },
47204
47205     /**
47206      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47207      * @param {Object} values
47208      * @return {BasicForm} this
47209      */
47210     applyIfToFields : function(o){
47211         this.items.each(function(f){
47212            Roo.applyIf(f, o);
47213         });
47214         return this;
47215     }
47216 });
47217
47218 // back compat
47219 Roo.BasicForm = Roo.form.BasicForm;/*
47220  * Based on:
47221  * Ext JS Library 1.1.1
47222  * Copyright(c) 2006-2007, Ext JS, LLC.
47223  *
47224  * Originally Released Under LGPL - original licence link has changed is not relivant.
47225  *
47226  * Fork - LGPL
47227  * <script type="text/javascript">
47228  */
47229
47230 /**
47231  * @class Roo.form.Form
47232  * @extends Roo.form.BasicForm
47233  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47234  * @constructor
47235  * @param {Object} config Configuration options
47236  */
47237 Roo.form.Form = function(config){
47238     var xitems =  [];
47239     if (config.items) {
47240         xitems = config.items;
47241         delete config.items;
47242     }
47243    
47244     
47245     Roo.form.Form.superclass.constructor.call(this, null, config);
47246     this.url = this.url || this.action;
47247     if(!this.root){
47248         this.root = new Roo.form.Layout(Roo.applyIf({
47249             id: Roo.id()
47250         }, config));
47251     }
47252     this.active = this.root;
47253     /**
47254      * Array of all the buttons that have been added to this form via {@link addButton}
47255      * @type Array
47256      */
47257     this.buttons = [];
47258     this.allItems = [];
47259     this.addEvents({
47260         /**
47261          * @event clientvalidation
47262          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47263          * @param {Form} this
47264          * @param {Boolean} valid true if the form has passed client-side validation
47265          */
47266         clientvalidation: true,
47267         /**
47268          * @event rendered
47269          * Fires when the form is rendered
47270          * @param {Roo.form.Form} form
47271          */
47272         rendered : true
47273     });
47274     
47275     if (this.progressUrl) {
47276             // push a hidden field onto the list of fields..
47277             this.addxtype( {
47278                     xns: Roo.form, 
47279                     xtype : 'Hidden', 
47280                     name : 'UPLOAD_IDENTIFIER' 
47281             });
47282         }
47283         
47284     
47285     Roo.each(xitems, this.addxtype, this);
47286     
47287     
47288     
47289 };
47290
47291 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47292     /**
47293      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47294      */
47295     /**
47296      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47297      */
47298     /**
47299      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47300      */
47301     buttonAlign:'center',
47302
47303     /**
47304      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47305      */
47306     minButtonWidth:75,
47307
47308     /**
47309      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47310      * This property cascades to child containers if not set.
47311      */
47312     labelAlign:'left',
47313
47314     /**
47315      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47316      * fires a looping event with that state. This is required to bind buttons to the valid
47317      * state using the config value formBind:true on the button.
47318      */
47319     monitorValid : false,
47320
47321     /**
47322      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47323      */
47324     monitorPoll : 200,
47325     
47326     /**
47327      * @cfg {String} progressUrl - Url to return progress data 
47328      */
47329     
47330     progressUrl : false,
47331   
47332     /**
47333      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47334      * fields are added and the column is closed. If no fields are passed the column remains open
47335      * until end() is called.
47336      * @param {Object} config The config to pass to the column
47337      * @param {Field} field1 (optional)
47338      * @param {Field} field2 (optional)
47339      * @param {Field} etc (optional)
47340      * @return Column The column container object
47341      */
47342     column : function(c){
47343         var col = new Roo.form.Column(c);
47344         this.start(col);
47345         if(arguments.length > 1){ // duplicate code required because of Opera
47346             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47347             this.end();
47348         }
47349         return col;
47350     },
47351
47352     /**
47353      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47354      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47355      * until end() is called.
47356      * @param {Object} config The config to pass to the fieldset
47357      * @param {Field} field1 (optional)
47358      * @param {Field} field2 (optional)
47359      * @param {Field} etc (optional)
47360      * @return FieldSet The fieldset container object
47361      */
47362     fieldset : function(c){
47363         var fs = new Roo.form.FieldSet(c);
47364         this.start(fs);
47365         if(arguments.length > 1){ // duplicate code required because of Opera
47366             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47367             this.end();
47368         }
47369         return fs;
47370     },
47371
47372     /**
47373      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47374      * fields are added and the container is closed. If no fields are passed the container remains open
47375      * until end() is called.
47376      * @param {Object} config The config to pass to the Layout
47377      * @param {Field} field1 (optional)
47378      * @param {Field} field2 (optional)
47379      * @param {Field} etc (optional)
47380      * @return Layout The container object
47381      */
47382     container : function(c){
47383         var l = new Roo.form.Layout(c);
47384         this.start(l);
47385         if(arguments.length > 1){ // duplicate code required because of Opera
47386             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47387             this.end();
47388         }
47389         return l;
47390     },
47391
47392     /**
47393      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47394      * @param {Object} container A Roo.form.Layout or subclass of Layout
47395      * @return {Form} this
47396      */
47397     start : function(c){
47398         // cascade label info
47399         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47400         this.active.stack.push(c);
47401         c.ownerCt = this.active;
47402         this.active = c;
47403         return this;
47404     },
47405
47406     /**
47407      * Closes the current open container
47408      * @return {Form} this
47409      */
47410     end : function(){
47411         if(this.active == this.root){
47412             return this;
47413         }
47414         this.active = this.active.ownerCt;
47415         return this;
47416     },
47417
47418     /**
47419      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47420      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47421      * as the label of the field.
47422      * @param {Field} field1
47423      * @param {Field} field2 (optional)
47424      * @param {Field} etc. (optional)
47425      * @return {Form} this
47426      */
47427     add : function(){
47428         this.active.stack.push.apply(this.active.stack, arguments);
47429         this.allItems.push.apply(this.allItems,arguments);
47430         var r = [];
47431         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47432             if(a[i].isFormField){
47433                 r.push(a[i]);
47434             }
47435         }
47436         if(r.length > 0){
47437             Roo.form.Form.superclass.add.apply(this, r);
47438         }
47439         return this;
47440     },
47441     
47442
47443     
47444     
47445     
47446      /**
47447      * Find any element that has been added to a form, using it's ID or name
47448      * This can include framesets, columns etc. along with regular fields..
47449      * @param {String} id - id or name to find.
47450      
47451      * @return {Element} e - or false if nothing found.
47452      */
47453     findbyId : function(id)
47454     {
47455         var ret = false;
47456         if (!id) {
47457             return ret;
47458         }
47459         Roo.each(this.allItems, function(f){
47460             if (f.id == id || f.name == id ){
47461                 ret = f;
47462                 return false;
47463             }
47464         });
47465         return ret;
47466     },
47467
47468     
47469     
47470     /**
47471      * Render this form into the passed container. This should only be called once!
47472      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47473      * @return {Form} this
47474      */
47475     render : function(ct)
47476     {
47477         
47478         
47479         
47480         ct = Roo.get(ct);
47481         var o = this.autoCreate || {
47482             tag: 'form',
47483             method : this.method || 'POST',
47484             id : this.id || Roo.id()
47485         };
47486         this.initEl(ct.createChild(o));
47487
47488         this.root.render(this.el);
47489         
47490        
47491              
47492         this.items.each(function(f){
47493             f.render('x-form-el-'+f.id);
47494         });
47495
47496         if(this.buttons.length > 0){
47497             // tables are required to maintain order and for correct IE layout
47498             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47499                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47500                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47501             }}, null, true);
47502             var tr = tb.getElementsByTagName('tr')[0];
47503             for(var i = 0, len = this.buttons.length; i < len; i++) {
47504                 var b = this.buttons[i];
47505                 var td = document.createElement('td');
47506                 td.className = 'x-form-btn-td';
47507                 b.render(tr.appendChild(td));
47508             }
47509         }
47510         if(this.monitorValid){ // initialize after render
47511             this.startMonitoring();
47512         }
47513         this.fireEvent('rendered', this);
47514         return this;
47515     },
47516
47517     /**
47518      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47519      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47520      * object or a valid Roo.DomHelper element config
47521      * @param {Function} handler The function called when the button is clicked
47522      * @param {Object} scope (optional) The scope of the handler function
47523      * @return {Roo.Button}
47524      */
47525     addButton : function(config, handler, scope){
47526         var bc = {
47527             handler: handler,
47528             scope: scope,
47529             minWidth: this.minButtonWidth,
47530             hideParent:true
47531         };
47532         if(typeof config == "string"){
47533             bc.text = config;
47534         }else{
47535             Roo.apply(bc, config);
47536         }
47537         var btn = new Roo.Button(null, bc);
47538         this.buttons.push(btn);
47539         return btn;
47540     },
47541
47542      /**
47543      * Adds a series of form elements (using the xtype property as the factory method.
47544      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47545      * @param {Object} config 
47546      */
47547     
47548     addxtype : function()
47549     {
47550         var ar = Array.prototype.slice.call(arguments, 0);
47551         var ret = false;
47552         for(var i = 0; i < ar.length; i++) {
47553             if (!ar[i]) {
47554                 continue; // skip -- if this happends something invalid got sent, we 
47555                 // should ignore it, as basically that interface element will not show up
47556                 // and that should be pretty obvious!!
47557             }
47558             
47559             if (Roo.form[ar[i].xtype]) {
47560                 ar[i].form = this;
47561                 var fe = Roo.factory(ar[i], Roo.form);
47562                 if (!ret) {
47563                     ret = fe;
47564                 }
47565                 fe.form = this;
47566                 if (fe.store) {
47567                     fe.store.form = this;
47568                 }
47569                 if (fe.isLayout) {  
47570                          
47571                     this.start(fe);
47572                     this.allItems.push(fe);
47573                     if (fe.items && fe.addxtype) {
47574                         fe.addxtype.apply(fe, fe.items);
47575                         delete fe.items;
47576                     }
47577                      this.end();
47578                     continue;
47579                 }
47580                 
47581                 
47582                  
47583                 this.add(fe);
47584               //  console.log('adding ' + ar[i].xtype);
47585             }
47586             if (ar[i].xtype == 'Button') {  
47587                 //console.log('adding button');
47588                 //console.log(ar[i]);
47589                 this.addButton(ar[i]);
47590                 this.allItems.push(fe);
47591                 continue;
47592             }
47593             
47594             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47595                 alert('end is not supported on xtype any more, use items');
47596             //    this.end();
47597             //    //console.log('adding end');
47598             }
47599             
47600         }
47601         return ret;
47602     },
47603     
47604     /**
47605      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47606      * option "monitorValid"
47607      */
47608     startMonitoring : function(){
47609         if(!this.bound){
47610             this.bound = true;
47611             Roo.TaskMgr.start({
47612                 run : this.bindHandler,
47613                 interval : this.monitorPoll || 200,
47614                 scope: this
47615             });
47616         }
47617     },
47618
47619     /**
47620      * Stops monitoring of the valid state of this form
47621      */
47622     stopMonitoring : function(){
47623         this.bound = false;
47624     },
47625
47626     // private
47627     bindHandler : function(){
47628         if(!this.bound){
47629             return false; // stops binding
47630         }
47631         var valid = true;
47632         this.items.each(function(f){
47633             if(!f.isValid(true)){
47634                 valid = false;
47635                 return false;
47636             }
47637         });
47638         for(var i = 0, len = this.buttons.length; i < len; i++){
47639             var btn = this.buttons[i];
47640             if(btn.formBind === true && btn.disabled === valid){
47641                 btn.setDisabled(!valid);
47642             }
47643         }
47644         this.fireEvent('clientvalidation', this, valid);
47645     }
47646     
47647     
47648     
47649     
47650     
47651     
47652     
47653     
47654 });
47655
47656
47657 // back compat
47658 Roo.Form = Roo.form.Form;
47659 /*
47660  * Based on:
47661  * Ext JS Library 1.1.1
47662  * Copyright(c) 2006-2007, Ext JS, LLC.
47663  *
47664  * Originally Released Under LGPL - original licence link has changed is not relivant.
47665  *
47666  * Fork - LGPL
47667  * <script type="text/javascript">
47668  */
47669
47670 // as we use this in bootstrap.
47671 Roo.namespace('Roo.form');
47672  /**
47673  * @class Roo.form.Action
47674  * Internal Class used to handle form actions
47675  * @constructor
47676  * @param {Roo.form.BasicForm} el The form element or its id
47677  * @param {Object} config Configuration options
47678  */
47679
47680  
47681  
47682 // define the action interface
47683 Roo.form.Action = function(form, options){
47684     this.form = form;
47685     this.options = options || {};
47686 };
47687 /**
47688  * Client Validation Failed
47689  * @const 
47690  */
47691 Roo.form.Action.CLIENT_INVALID = 'client';
47692 /**
47693  * Server Validation Failed
47694  * @const 
47695  */
47696 Roo.form.Action.SERVER_INVALID = 'server';
47697  /**
47698  * Connect to Server Failed
47699  * @const 
47700  */
47701 Roo.form.Action.CONNECT_FAILURE = 'connect';
47702 /**
47703  * Reading Data from Server Failed
47704  * @const 
47705  */
47706 Roo.form.Action.LOAD_FAILURE = 'load';
47707
47708 Roo.form.Action.prototype = {
47709     type : 'default',
47710     failureType : undefined,
47711     response : undefined,
47712     result : undefined,
47713
47714     // interface method
47715     run : function(options){
47716
47717     },
47718
47719     // interface method
47720     success : function(response){
47721
47722     },
47723
47724     // interface method
47725     handleResponse : function(response){
47726
47727     },
47728
47729     // default connection failure
47730     failure : function(response){
47731         
47732         this.response = response;
47733         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47734         this.form.afterAction(this, false);
47735     },
47736
47737     processResponse : function(response){
47738         this.response = response;
47739         if(!response.responseText){
47740             return true;
47741         }
47742         this.result = this.handleResponse(response);
47743         return this.result;
47744     },
47745
47746     // utility functions used internally
47747     getUrl : function(appendParams){
47748         var url = this.options.url || this.form.url || this.form.el.dom.action;
47749         if(appendParams){
47750             var p = this.getParams();
47751             if(p){
47752                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47753             }
47754         }
47755         return url;
47756     },
47757
47758     getMethod : function(){
47759         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47760     },
47761
47762     getParams : function(){
47763         var bp = this.form.baseParams;
47764         var p = this.options.params;
47765         if(p){
47766             if(typeof p == "object"){
47767                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47768             }else if(typeof p == 'string' && bp){
47769                 p += '&' + Roo.urlEncode(bp);
47770             }
47771         }else if(bp){
47772             p = Roo.urlEncode(bp);
47773         }
47774         return p;
47775     },
47776
47777     createCallback : function(){
47778         return {
47779             success: this.success,
47780             failure: this.failure,
47781             scope: this,
47782             timeout: (this.form.timeout*1000),
47783             upload: this.form.fileUpload ? this.success : undefined
47784         };
47785     }
47786 };
47787
47788 Roo.form.Action.Submit = function(form, options){
47789     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47790 };
47791
47792 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47793     type : 'submit',
47794
47795     haveProgress : false,
47796     uploadComplete : false,
47797     
47798     // uploadProgress indicator.
47799     uploadProgress : function()
47800     {
47801         if (!this.form.progressUrl) {
47802             return;
47803         }
47804         
47805         if (!this.haveProgress) {
47806             Roo.MessageBox.progress("Uploading", "Uploading");
47807         }
47808         if (this.uploadComplete) {
47809            Roo.MessageBox.hide();
47810            return;
47811         }
47812         
47813         this.haveProgress = true;
47814    
47815         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47816         
47817         var c = new Roo.data.Connection();
47818         c.request({
47819             url : this.form.progressUrl,
47820             params: {
47821                 id : uid
47822             },
47823             method: 'GET',
47824             success : function(req){
47825                //console.log(data);
47826                 var rdata = false;
47827                 var edata;
47828                 try  {
47829                    rdata = Roo.decode(req.responseText)
47830                 } catch (e) {
47831                     Roo.log("Invalid data from server..");
47832                     Roo.log(edata);
47833                     return;
47834                 }
47835                 if (!rdata || !rdata.success) {
47836                     Roo.log(rdata);
47837                     Roo.MessageBox.alert(Roo.encode(rdata));
47838                     return;
47839                 }
47840                 var data = rdata.data;
47841                 
47842                 if (this.uploadComplete) {
47843                    Roo.MessageBox.hide();
47844                    return;
47845                 }
47846                    
47847                 if (data){
47848                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47849                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47850                     );
47851                 }
47852                 this.uploadProgress.defer(2000,this);
47853             },
47854        
47855             failure: function(data) {
47856                 Roo.log('progress url failed ');
47857                 Roo.log(data);
47858             },
47859             scope : this
47860         });
47861            
47862     },
47863     
47864     
47865     run : function()
47866     {
47867         // run get Values on the form, so it syncs any secondary forms.
47868         this.form.getValues();
47869         
47870         var o = this.options;
47871         var method = this.getMethod();
47872         var isPost = method == 'POST';
47873         if(o.clientValidation === false || this.form.isValid()){
47874             
47875             if (this.form.progressUrl) {
47876                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47877                     (new Date() * 1) + '' + Math.random());
47878                     
47879             } 
47880             
47881             
47882             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47883                 form:this.form.el.dom,
47884                 url:this.getUrl(!isPost),
47885                 method: method,
47886                 params:isPost ? this.getParams() : null,
47887                 isUpload: this.form.fileUpload
47888             }));
47889             
47890             this.uploadProgress();
47891
47892         }else if (o.clientValidation !== false){ // client validation failed
47893             this.failureType = Roo.form.Action.CLIENT_INVALID;
47894             this.form.afterAction(this, false);
47895         }
47896     },
47897
47898     success : function(response)
47899     {
47900         this.uploadComplete= true;
47901         if (this.haveProgress) {
47902             Roo.MessageBox.hide();
47903         }
47904         
47905         
47906         var result = this.processResponse(response);
47907         if(result === true || result.success){
47908             this.form.afterAction(this, true);
47909             return;
47910         }
47911         if(result.errors){
47912             this.form.markInvalid(result.errors);
47913             this.failureType = Roo.form.Action.SERVER_INVALID;
47914         }
47915         this.form.afterAction(this, false);
47916     },
47917     failure : function(response)
47918     {
47919         this.uploadComplete= true;
47920         if (this.haveProgress) {
47921             Roo.MessageBox.hide();
47922         }
47923         
47924         this.response = response;
47925         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47926         this.form.afterAction(this, false);
47927     },
47928     
47929     handleResponse : function(response){
47930         if(this.form.errorReader){
47931             var rs = this.form.errorReader.read(response);
47932             var errors = [];
47933             if(rs.records){
47934                 for(var i = 0, len = rs.records.length; i < len; i++) {
47935                     var r = rs.records[i];
47936                     errors[i] = r.data;
47937                 }
47938             }
47939             if(errors.length < 1){
47940                 errors = null;
47941             }
47942             return {
47943                 success : rs.success,
47944                 errors : errors
47945             };
47946         }
47947         var ret = false;
47948         try {
47949             ret = Roo.decode(response.responseText);
47950         } catch (e) {
47951             ret = {
47952                 success: false,
47953                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47954                 errors : []
47955             };
47956         }
47957         return ret;
47958         
47959     }
47960 });
47961
47962
47963 Roo.form.Action.Load = function(form, options){
47964     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47965     this.reader = this.form.reader;
47966 };
47967
47968 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47969     type : 'load',
47970
47971     run : function(){
47972         
47973         Roo.Ajax.request(Roo.apply(
47974                 this.createCallback(), {
47975                     method:this.getMethod(),
47976                     url:this.getUrl(false),
47977                     params:this.getParams()
47978         }));
47979     },
47980
47981     success : function(response){
47982         
47983         var result = this.processResponse(response);
47984         if(result === true || !result.success || !result.data){
47985             this.failureType = Roo.form.Action.LOAD_FAILURE;
47986             this.form.afterAction(this, false);
47987             return;
47988         }
47989         this.form.clearInvalid();
47990         this.form.setValues(result.data);
47991         this.form.afterAction(this, true);
47992     },
47993
47994     handleResponse : function(response){
47995         if(this.form.reader){
47996             var rs = this.form.reader.read(response);
47997             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47998             return {
47999                 success : rs.success,
48000                 data : data
48001             };
48002         }
48003         return Roo.decode(response.responseText);
48004     }
48005 });
48006
48007 Roo.form.Action.ACTION_TYPES = {
48008     'load' : Roo.form.Action.Load,
48009     'submit' : Roo.form.Action.Submit
48010 };/*
48011  * Based on:
48012  * Ext JS Library 1.1.1
48013  * Copyright(c) 2006-2007, Ext JS, LLC.
48014  *
48015  * Originally Released Under LGPL - original licence link has changed is not relivant.
48016  *
48017  * Fork - LGPL
48018  * <script type="text/javascript">
48019  */
48020  
48021 /**
48022  * @class Roo.form.Layout
48023  * @extends Roo.Component
48024  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48025  * @constructor
48026  * @param {Object} config Configuration options
48027  */
48028 Roo.form.Layout = function(config){
48029     var xitems = [];
48030     if (config.items) {
48031         xitems = config.items;
48032         delete config.items;
48033     }
48034     Roo.form.Layout.superclass.constructor.call(this, config);
48035     this.stack = [];
48036     Roo.each(xitems, this.addxtype, this);
48037      
48038 };
48039
48040 Roo.extend(Roo.form.Layout, Roo.Component, {
48041     /**
48042      * @cfg {String/Object} autoCreate
48043      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48044      */
48045     /**
48046      * @cfg {String/Object/Function} style
48047      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48048      * a function which returns such a specification.
48049      */
48050     /**
48051      * @cfg {String} labelAlign
48052      * Valid values are "left," "top" and "right" (defaults to "left")
48053      */
48054     /**
48055      * @cfg {Number} labelWidth
48056      * Fixed width in pixels of all field labels (defaults to undefined)
48057      */
48058     /**
48059      * @cfg {Boolean} clear
48060      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48061      */
48062     clear : true,
48063     /**
48064      * @cfg {String} labelSeparator
48065      * The separator to use after field labels (defaults to ':')
48066      */
48067     labelSeparator : ':',
48068     /**
48069      * @cfg {Boolean} hideLabels
48070      * True to suppress the display of field labels in this layout (defaults to false)
48071      */
48072     hideLabels : false,
48073
48074     // private
48075     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48076     
48077     isLayout : true,
48078     
48079     // private
48080     onRender : function(ct, position){
48081         if(this.el){ // from markup
48082             this.el = Roo.get(this.el);
48083         }else {  // generate
48084             var cfg = this.getAutoCreate();
48085             this.el = ct.createChild(cfg, position);
48086         }
48087         if(this.style){
48088             this.el.applyStyles(this.style);
48089         }
48090         if(this.labelAlign){
48091             this.el.addClass('x-form-label-'+this.labelAlign);
48092         }
48093         if(this.hideLabels){
48094             this.labelStyle = "display:none";
48095             this.elementStyle = "padding-left:0;";
48096         }else{
48097             if(typeof this.labelWidth == 'number'){
48098                 this.labelStyle = "width:"+this.labelWidth+"px;";
48099                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48100             }
48101             if(this.labelAlign == 'top'){
48102                 this.labelStyle = "width:auto;";
48103                 this.elementStyle = "padding-left:0;";
48104             }
48105         }
48106         var stack = this.stack;
48107         var slen = stack.length;
48108         if(slen > 0){
48109             if(!this.fieldTpl){
48110                 var t = new Roo.Template(
48111                     '<div class="x-form-item {5}">',
48112                         '<label for="{0}" style="{2}">{1}{4}</label>',
48113                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48114                         '</div>',
48115                     '</div><div class="x-form-clear-left"></div>'
48116                 );
48117                 t.disableFormats = true;
48118                 t.compile();
48119                 Roo.form.Layout.prototype.fieldTpl = t;
48120             }
48121             for(var i = 0; i < slen; i++) {
48122                 if(stack[i].isFormField){
48123                     this.renderField(stack[i]);
48124                 }else{
48125                     this.renderComponent(stack[i]);
48126                 }
48127             }
48128         }
48129         if(this.clear){
48130             this.el.createChild({cls:'x-form-clear'});
48131         }
48132     },
48133
48134     // private
48135     renderField : function(f){
48136         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48137                f.id, //0
48138                f.fieldLabel, //1
48139                f.labelStyle||this.labelStyle||'', //2
48140                this.elementStyle||'', //3
48141                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48142                f.itemCls||this.itemCls||''  //5
48143        ], true).getPrevSibling());
48144     },
48145
48146     // private
48147     renderComponent : function(c){
48148         c.render(c.isLayout ? this.el : this.el.createChild());    
48149     },
48150     /**
48151      * Adds a object form elements (using the xtype property as the factory method.)
48152      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48153      * @param {Object} config 
48154      */
48155     addxtype : function(o)
48156     {
48157         // create the lement.
48158         o.form = this.form;
48159         var fe = Roo.factory(o, Roo.form);
48160         this.form.allItems.push(fe);
48161         this.stack.push(fe);
48162         
48163         if (fe.isFormField) {
48164             this.form.items.add(fe);
48165         }
48166          
48167         return fe;
48168     }
48169 });
48170
48171 /**
48172  * @class Roo.form.Column
48173  * @extends Roo.form.Layout
48174  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48175  * @constructor
48176  * @param {Object} config Configuration options
48177  */
48178 Roo.form.Column = function(config){
48179     Roo.form.Column.superclass.constructor.call(this, config);
48180 };
48181
48182 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48183     /**
48184      * @cfg {Number/String} width
48185      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48186      */
48187     /**
48188      * @cfg {String/Object} autoCreate
48189      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48190      */
48191
48192     // private
48193     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48194
48195     // private
48196     onRender : function(ct, position){
48197         Roo.form.Column.superclass.onRender.call(this, ct, position);
48198         if(this.width){
48199             this.el.setWidth(this.width);
48200         }
48201     }
48202 });
48203
48204
48205 /**
48206  * @class Roo.form.Row
48207  * @extends Roo.form.Layout
48208  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48209  * @constructor
48210  * @param {Object} config Configuration options
48211  */
48212
48213  
48214 Roo.form.Row = function(config){
48215     Roo.form.Row.superclass.constructor.call(this, config);
48216 };
48217  
48218 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48219       /**
48220      * @cfg {Number/String} width
48221      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48222      */
48223     /**
48224      * @cfg {Number/String} height
48225      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48226      */
48227     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48228     
48229     padWidth : 20,
48230     // private
48231     onRender : function(ct, position){
48232         //console.log('row render');
48233         if(!this.rowTpl){
48234             var t = new Roo.Template(
48235                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48236                     '<label for="{0}" style="{2}">{1}{4}</label>',
48237                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48238                     '</div>',
48239                 '</div>'
48240             );
48241             t.disableFormats = true;
48242             t.compile();
48243             Roo.form.Layout.prototype.rowTpl = t;
48244         }
48245         this.fieldTpl = this.rowTpl;
48246         
48247         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48248         var labelWidth = 100;
48249         
48250         if ((this.labelAlign != 'top')) {
48251             if (typeof this.labelWidth == 'number') {
48252                 labelWidth = this.labelWidth
48253             }
48254             this.padWidth =  20 + labelWidth;
48255             
48256         }
48257         
48258         Roo.form.Column.superclass.onRender.call(this, ct, position);
48259         if(this.width){
48260             this.el.setWidth(this.width);
48261         }
48262         if(this.height){
48263             this.el.setHeight(this.height);
48264         }
48265     },
48266     
48267     // private
48268     renderField : function(f){
48269         f.fieldEl = this.fieldTpl.append(this.el, [
48270                f.id, f.fieldLabel,
48271                f.labelStyle||this.labelStyle||'',
48272                this.elementStyle||'',
48273                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48274                f.itemCls||this.itemCls||'',
48275                f.width ? f.width + this.padWidth : 160 + this.padWidth
48276        ],true);
48277     }
48278 });
48279  
48280
48281 /**
48282  * @class Roo.form.FieldSet
48283  * @extends Roo.form.Layout
48284  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48285  * @constructor
48286  * @param {Object} config Configuration options
48287  */
48288 Roo.form.FieldSet = function(config){
48289     Roo.form.FieldSet.superclass.constructor.call(this, config);
48290 };
48291
48292 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48293     /**
48294      * @cfg {String} legend
48295      * The text to display as the legend for the FieldSet (defaults to '')
48296      */
48297     /**
48298      * @cfg {String/Object} autoCreate
48299      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48300      */
48301
48302     // private
48303     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48304
48305     // private
48306     onRender : function(ct, position){
48307         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48308         if(this.legend){
48309             this.setLegend(this.legend);
48310         }
48311     },
48312
48313     // private
48314     setLegend : function(text){
48315         if(this.rendered){
48316             this.el.child('legend').update(text);
48317         }
48318     }
48319 });/*
48320  * Based on:
48321  * Ext JS Library 1.1.1
48322  * Copyright(c) 2006-2007, Ext JS, LLC.
48323  *
48324  * Originally Released Under LGPL - original licence link has changed is not relivant.
48325  *
48326  * Fork - LGPL
48327  * <script type="text/javascript">
48328  */
48329 /**
48330  * @class Roo.form.VTypes
48331  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48332  * @singleton
48333  */
48334 Roo.form.VTypes = function(){
48335     // closure these in so they are only created once.
48336     var alpha = /^[a-zA-Z_]+$/;
48337     var alphanum = /^[a-zA-Z0-9_]+$/;
48338     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48339     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48340
48341     // All these messages and functions are configurable
48342     return {
48343         /**
48344          * The function used to validate email addresses
48345          * @param {String} value The email address
48346          */
48347         'email' : function(v){
48348             return email.test(v);
48349         },
48350         /**
48351          * The error text to display when the email validation function returns false
48352          * @type String
48353          */
48354         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48355         /**
48356          * The keystroke filter mask to be applied on email input
48357          * @type RegExp
48358          */
48359         'emailMask' : /[a-z0-9_\.\-@]/i,
48360
48361         /**
48362          * The function used to validate URLs
48363          * @param {String} value The URL
48364          */
48365         'url' : function(v){
48366             return url.test(v);
48367         },
48368         /**
48369          * The error text to display when the url validation function returns false
48370          * @type String
48371          */
48372         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48373         
48374         /**
48375          * The function used to validate alpha values
48376          * @param {String} value The value
48377          */
48378         'alpha' : function(v){
48379             return alpha.test(v);
48380         },
48381         /**
48382          * The error text to display when the alpha validation function returns false
48383          * @type String
48384          */
48385         'alphaText' : 'This field should only contain letters and _',
48386         /**
48387          * The keystroke filter mask to be applied on alpha input
48388          * @type RegExp
48389          */
48390         'alphaMask' : /[a-z_]/i,
48391
48392         /**
48393          * The function used to validate alphanumeric values
48394          * @param {String} value The value
48395          */
48396         'alphanum' : function(v){
48397             return alphanum.test(v);
48398         },
48399         /**
48400          * The error text to display when the alphanumeric validation function returns false
48401          * @type String
48402          */
48403         'alphanumText' : 'This field should only contain letters, numbers and _',
48404         /**
48405          * The keystroke filter mask to be applied on alphanumeric input
48406          * @type RegExp
48407          */
48408         'alphanumMask' : /[a-z0-9_]/i
48409     };
48410 }();//<script type="text/javascript">
48411
48412 /**
48413  * @class Roo.form.FCKeditor
48414  * @extends Roo.form.TextArea
48415  * Wrapper around the FCKEditor http://www.fckeditor.net
48416  * @constructor
48417  * Creates a new FCKeditor
48418  * @param {Object} config Configuration options
48419  */
48420 Roo.form.FCKeditor = function(config){
48421     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48422     this.addEvents({
48423          /**
48424          * @event editorinit
48425          * Fired when the editor is initialized - you can add extra handlers here..
48426          * @param {FCKeditor} this
48427          * @param {Object} the FCK object.
48428          */
48429         editorinit : true
48430     });
48431     
48432     
48433 };
48434 Roo.form.FCKeditor.editors = { };
48435 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48436 {
48437     //defaultAutoCreate : {
48438     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48439     //},
48440     // private
48441     /**
48442      * @cfg {Object} fck options - see fck manual for details.
48443      */
48444     fckconfig : false,
48445     
48446     /**
48447      * @cfg {Object} fck toolbar set (Basic or Default)
48448      */
48449     toolbarSet : 'Basic',
48450     /**
48451      * @cfg {Object} fck BasePath
48452      */ 
48453     basePath : '/fckeditor/',
48454     
48455     
48456     frame : false,
48457     
48458     value : '',
48459     
48460    
48461     onRender : function(ct, position)
48462     {
48463         if(!this.el){
48464             this.defaultAutoCreate = {
48465                 tag: "textarea",
48466                 style:"width:300px;height:60px;",
48467                 autocomplete: "new-password"
48468             };
48469         }
48470         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48471         /*
48472         if(this.grow){
48473             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48474             if(this.preventScrollbars){
48475                 this.el.setStyle("overflow", "hidden");
48476             }
48477             this.el.setHeight(this.growMin);
48478         }
48479         */
48480         //console.log('onrender' + this.getId() );
48481         Roo.form.FCKeditor.editors[this.getId()] = this;
48482          
48483
48484         this.replaceTextarea() ;
48485         
48486     },
48487     
48488     getEditor : function() {
48489         return this.fckEditor;
48490     },
48491     /**
48492      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48493      * @param {Mixed} value The value to set
48494      */
48495     
48496     
48497     setValue : function(value)
48498     {
48499         //console.log('setValue: ' + value);
48500         
48501         if(typeof(value) == 'undefined') { // not sure why this is happending...
48502             return;
48503         }
48504         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48505         
48506         //if(!this.el || !this.getEditor()) {
48507         //    this.value = value;
48508             //this.setValue.defer(100,this,[value]);    
48509         //    return;
48510         //} 
48511         
48512         if(!this.getEditor()) {
48513             return;
48514         }
48515         
48516         this.getEditor().SetData(value);
48517         
48518         //
48519
48520     },
48521
48522     /**
48523      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48524      * @return {Mixed} value The field value
48525      */
48526     getValue : function()
48527     {
48528         
48529         if (this.frame && this.frame.dom.style.display == 'none') {
48530             return Roo.form.FCKeditor.superclass.getValue.call(this);
48531         }
48532         
48533         if(!this.el || !this.getEditor()) {
48534            
48535            // this.getValue.defer(100,this); 
48536             return this.value;
48537         }
48538        
48539         
48540         var value=this.getEditor().GetData();
48541         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48542         return Roo.form.FCKeditor.superclass.getValue.call(this);
48543         
48544
48545     },
48546
48547     /**
48548      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48549      * @return {Mixed} value The field value
48550      */
48551     getRawValue : function()
48552     {
48553         if (this.frame && this.frame.dom.style.display == 'none') {
48554             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48555         }
48556         
48557         if(!this.el || !this.getEditor()) {
48558             //this.getRawValue.defer(100,this); 
48559             return this.value;
48560             return;
48561         }
48562         
48563         
48564         
48565         var value=this.getEditor().GetData();
48566         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48567         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48568          
48569     },
48570     
48571     setSize : function(w,h) {
48572         
48573         
48574         
48575         //if (this.frame && this.frame.dom.style.display == 'none') {
48576         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48577         //    return;
48578         //}
48579         //if(!this.el || !this.getEditor()) {
48580         //    this.setSize.defer(100,this, [w,h]); 
48581         //    return;
48582         //}
48583         
48584         
48585         
48586         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48587         
48588         this.frame.dom.setAttribute('width', w);
48589         this.frame.dom.setAttribute('height', h);
48590         this.frame.setSize(w,h);
48591         
48592     },
48593     
48594     toggleSourceEdit : function(value) {
48595         
48596       
48597          
48598         this.el.dom.style.display = value ? '' : 'none';
48599         this.frame.dom.style.display = value ?  'none' : '';
48600         
48601     },
48602     
48603     
48604     focus: function(tag)
48605     {
48606         if (this.frame.dom.style.display == 'none') {
48607             return Roo.form.FCKeditor.superclass.focus.call(this);
48608         }
48609         if(!this.el || !this.getEditor()) {
48610             this.focus.defer(100,this, [tag]); 
48611             return;
48612         }
48613         
48614         
48615         
48616         
48617         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48618         this.getEditor().Focus();
48619         if (tgs.length) {
48620             if (!this.getEditor().Selection.GetSelection()) {
48621                 this.focus.defer(100,this, [tag]); 
48622                 return;
48623             }
48624             
48625             
48626             var r = this.getEditor().EditorDocument.createRange();
48627             r.setStart(tgs[0],0);
48628             r.setEnd(tgs[0],0);
48629             this.getEditor().Selection.GetSelection().removeAllRanges();
48630             this.getEditor().Selection.GetSelection().addRange(r);
48631             this.getEditor().Focus();
48632         }
48633         
48634     },
48635     
48636     
48637     
48638     replaceTextarea : function()
48639     {
48640         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48641             return ;
48642         }
48643         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48644         //{
48645             // We must check the elements firstly using the Id and then the name.
48646         var oTextarea = document.getElementById( this.getId() );
48647         
48648         var colElementsByName = document.getElementsByName( this.getId() ) ;
48649          
48650         oTextarea.style.display = 'none' ;
48651
48652         if ( oTextarea.tabIndex ) {            
48653             this.TabIndex = oTextarea.tabIndex ;
48654         }
48655         
48656         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48657         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48658         this.frame = Roo.get(this.getId() + '___Frame')
48659     },
48660     
48661     _getConfigHtml : function()
48662     {
48663         var sConfig = '' ;
48664
48665         for ( var o in this.fckconfig ) {
48666             sConfig += sConfig.length > 0  ? '&amp;' : '';
48667             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48668         }
48669
48670         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48671     },
48672     
48673     
48674     _getIFrameHtml : function()
48675     {
48676         var sFile = 'fckeditor.html' ;
48677         /* no idea what this is about..
48678         try
48679         {
48680             if ( (/fcksource=true/i).test( window.top.location.search ) )
48681                 sFile = 'fckeditor.original.html' ;
48682         }
48683         catch (e) { 
48684         */
48685
48686         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48687         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48688         
48689         
48690         var html = '<iframe id="' + this.getId() +
48691             '___Frame" src="' + sLink +
48692             '" width="' + this.width +
48693             '" height="' + this.height + '"' +
48694             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48695             ' frameborder="0" scrolling="no"></iframe>' ;
48696
48697         return html ;
48698     },
48699     
48700     _insertHtmlBefore : function( html, element )
48701     {
48702         if ( element.insertAdjacentHTML )       {
48703             // IE
48704             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48705         } else { // Gecko
48706             var oRange = document.createRange() ;
48707             oRange.setStartBefore( element ) ;
48708             var oFragment = oRange.createContextualFragment( html );
48709             element.parentNode.insertBefore( oFragment, element ) ;
48710         }
48711     }
48712     
48713     
48714   
48715     
48716     
48717     
48718     
48719
48720 });
48721
48722 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48723
48724 function FCKeditor_OnComplete(editorInstance){
48725     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48726     f.fckEditor = editorInstance;
48727     //console.log("loaded");
48728     f.fireEvent('editorinit', f, editorInstance);
48729
48730   
48731
48732  
48733
48734
48735
48736
48737
48738
48739
48740
48741
48742
48743
48744
48745
48746
48747
48748 //<script type="text/javascript">
48749 /**
48750  * @class Roo.form.GridField
48751  * @extends Roo.form.Field
48752  * Embed a grid (or editable grid into a form)
48753  * STATUS ALPHA
48754  * 
48755  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48756  * it needs 
48757  * xgrid.store = Roo.data.Store
48758  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48759  * xgrid.store.reader = Roo.data.JsonReader 
48760  * 
48761  * 
48762  * @constructor
48763  * Creates a new GridField
48764  * @param {Object} config Configuration options
48765  */
48766 Roo.form.GridField = function(config){
48767     Roo.form.GridField.superclass.constructor.call(this, config);
48768      
48769 };
48770
48771 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48772     /**
48773      * @cfg {Number} width  - used to restrict width of grid..
48774      */
48775     width : 100,
48776     /**
48777      * @cfg {Number} height - used to restrict height of grid..
48778      */
48779     height : 50,
48780      /**
48781      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48782          * 
48783          *}
48784      */
48785     xgrid : false, 
48786     /**
48787      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48788      * {tag: "input", type: "checkbox", autocomplete: "off"})
48789      */
48790    // defaultAutoCreate : { tag: 'div' },
48791     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48792     /**
48793      * @cfg {String} addTitle Text to include for adding a title.
48794      */
48795     addTitle : false,
48796     //
48797     onResize : function(){
48798         Roo.form.Field.superclass.onResize.apply(this, arguments);
48799     },
48800
48801     initEvents : function(){
48802         // Roo.form.Checkbox.superclass.initEvents.call(this);
48803         // has no events...
48804        
48805     },
48806
48807
48808     getResizeEl : function(){
48809         return this.wrap;
48810     },
48811
48812     getPositionEl : function(){
48813         return this.wrap;
48814     },
48815
48816     // private
48817     onRender : function(ct, position){
48818         
48819         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48820         var style = this.style;
48821         delete this.style;
48822         
48823         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48824         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48825         this.viewEl = this.wrap.createChild({ tag: 'div' });
48826         if (style) {
48827             this.viewEl.applyStyles(style);
48828         }
48829         if (this.width) {
48830             this.viewEl.setWidth(this.width);
48831         }
48832         if (this.height) {
48833             this.viewEl.setHeight(this.height);
48834         }
48835         //if(this.inputValue !== undefined){
48836         //this.setValue(this.value);
48837         
48838         
48839         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48840         
48841         
48842         this.grid.render();
48843         this.grid.getDataSource().on('remove', this.refreshValue, this);
48844         this.grid.getDataSource().on('update', this.refreshValue, this);
48845         this.grid.on('afteredit', this.refreshValue, this);
48846  
48847     },
48848      
48849     
48850     /**
48851      * Sets the value of the item. 
48852      * @param {String} either an object  or a string..
48853      */
48854     setValue : function(v){
48855         //this.value = v;
48856         v = v || []; // empty set..
48857         // this does not seem smart - it really only affects memoryproxy grids..
48858         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48859             var ds = this.grid.getDataSource();
48860             // assumes a json reader..
48861             var data = {}
48862             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48863             ds.loadData( data);
48864         }
48865         // clear selection so it does not get stale.
48866         if (this.grid.sm) { 
48867             this.grid.sm.clearSelections();
48868         }
48869         
48870         Roo.form.GridField.superclass.setValue.call(this, v);
48871         this.refreshValue();
48872         // should load data in the grid really....
48873     },
48874     
48875     // private
48876     refreshValue: function() {
48877          var val = [];
48878         this.grid.getDataSource().each(function(r) {
48879             val.push(r.data);
48880         });
48881         this.el.dom.value = Roo.encode(val);
48882     }
48883     
48884      
48885     
48886     
48887 });/*
48888  * Based on:
48889  * Ext JS Library 1.1.1
48890  * Copyright(c) 2006-2007, Ext JS, LLC.
48891  *
48892  * Originally Released Under LGPL - original licence link has changed is not relivant.
48893  *
48894  * Fork - LGPL
48895  * <script type="text/javascript">
48896  */
48897 /**
48898  * @class Roo.form.DisplayField
48899  * @extends Roo.form.Field
48900  * A generic Field to display non-editable data.
48901  * @cfg {Boolean} closable (true|false) default false
48902  * @constructor
48903  * Creates a new Display Field item.
48904  * @param {Object} config Configuration options
48905  */
48906 Roo.form.DisplayField = function(config){
48907     Roo.form.DisplayField.superclass.constructor.call(this, config);
48908     
48909     this.addEvents({
48910         /**
48911          * @event close
48912          * Fires after the click the close btn
48913              * @param {Roo.form.DisplayField} this
48914              */
48915         close : true
48916     });
48917 };
48918
48919 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48920     inputType:      'hidden',
48921     allowBlank:     true,
48922     readOnly:         true,
48923     
48924  
48925     /**
48926      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48927      */
48928     focusClass : undefined,
48929     /**
48930      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48931      */
48932     fieldClass: 'x-form-field',
48933     
48934      /**
48935      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48936      */
48937     valueRenderer: undefined,
48938     
48939     width: 100,
48940     /**
48941      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48942      * {tag: "input", type: "checkbox", autocomplete: "off"})
48943      */
48944      
48945  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48946  
48947     closable : false,
48948     
48949     onResize : function(){
48950         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48951         
48952     },
48953
48954     initEvents : function(){
48955         // Roo.form.Checkbox.superclass.initEvents.call(this);
48956         // has no events...
48957         
48958         if(this.closable){
48959             this.closeEl.on('click', this.onClose, this);
48960         }
48961        
48962     },
48963
48964
48965     getResizeEl : function(){
48966         return this.wrap;
48967     },
48968
48969     getPositionEl : function(){
48970         return this.wrap;
48971     },
48972
48973     // private
48974     onRender : function(ct, position){
48975         
48976         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48977         //if(this.inputValue !== undefined){
48978         this.wrap = this.el.wrap();
48979         
48980         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48981         
48982         if(this.closable){
48983             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48984         }
48985         
48986         if (this.bodyStyle) {
48987             this.viewEl.applyStyles(this.bodyStyle);
48988         }
48989         //this.viewEl.setStyle('padding', '2px');
48990         
48991         this.setValue(this.value);
48992         
48993     },
48994 /*
48995     // private
48996     initValue : Roo.emptyFn,
48997
48998   */
48999
49000         // private
49001     onClick : function(){
49002         
49003     },
49004
49005     /**
49006      * Sets the checked state of the checkbox.
49007      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49008      */
49009     setValue : function(v){
49010         this.value = v;
49011         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49012         // this might be called before we have a dom element..
49013         if (!this.viewEl) {
49014             return;
49015         }
49016         this.viewEl.dom.innerHTML = html;
49017         Roo.form.DisplayField.superclass.setValue.call(this, v);
49018
49019     },
49020     
49021     onClose : function(e)
49022     {
49023         e.preventDefault();
49024         
49025         this.fireEvent('close', this);
49026     }
49027 });/*
49028  * 
49029  * Licence- LGPL
49030  * 
49031  */
49032
49033 /**
49034  * @class Roo.form.DayPicker
49035  * @extends Roo.form.Field
49036  * A Day picker show [M] [T] [W] ....
49037  * @constructor
49038  * Creates a new Day Picker
49039  * @param {Object} config Configuration options
49040  */
49041 Roo.form.DayPicker= function(config){
49042     Roo.form.DayPicker.superclass.constructor.call(this, config);
49043      
49044 };
49045
49046 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49047     /**
49048      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49049      */
49050     focusClass : undefined,
49051     /**
49052      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49053      */
49054     fieldClass: "x-form-field",
49055    
49056     /**
49057      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49058      * {tag: "input", type: "checkbox", autocomplete: "off"})
49059      */
49060     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49061     
49062    
49063     actionMode : 'viewEl', 
49064     //
49065     // private
49066  
49067     inputType : 'hidden',
49068     
49069      
49070     inputElement: false, // real input element?
49071     basedOn: false, // ????
49072     
49073     isFormField: true, // not sure where this is needed!!!!
49074
49075     onResize : function(){
49076         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49077         if(!this.boxLabel){
49078             this.el.alignTo(this.wrap, 'c-c');
49079         }
49080     },
49081
49082     initEvents : function(){
49083         Roo.form.Checkbox.superclass.initEvents.call(this);
49084         this.el.on("click", this.onClick,  this);
49085         this.el.on("change", this.onClick,  this);
49086     },
49087
49088
49089     getResizeEl : function(){
49090         return this.wrap;
49091     },
49092
49093     getPositionEl : function(){
49094         return this.wrap;
49095     },
49096
49097     
49098     // private
49099     onRender : function(ct, position){
49100         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49101        
49102         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49103         
49104         var r1 = '<table><tr>';
49105         var r2 = '<tr class="x-form-daypick-icons">';
49106         for (var i=0; i < 7; i++) {
49107             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49108             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49109         }
49110         
49111         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49112         viewEl.select('img').on('click', this.onClick, this);
49113         this.viewEl = viewEl;   
49114         
49115         
49116         // this will not work on Chrome!!!
49117         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49118         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49119         
49120         
49121           
49122
49123     },
49124
49125     // private
49126     initValue : Roo.emptyFn,
49127
49128     /**
49129      * Returns the checked state of the checkbox.
49130      * @return {Boolean} True if checked, else false
49131      */
49132     getValue : function(){
49133         return this.el.dom.value;
49134         
49135     },
49136
49137         // private
49138     onClick : function(e){ 
49139         //this.setChecked(!this.checked);
49140         Roo.get(e.target).toggleClass('x-menu-item-checked');
49141         this.refreshValue();
49142         //if(this.el.dom.checked != this.checked){
49143         //    this.setValue(this.el.dom.checked);
49144        // }
49145     },
49146     
49147     // private
49148     refreshValue : function()
49149     {
49150         var val = '';
49151         this.viewEl.select('img',true).each(function(e,i,n)  {
49152             val += e.is(".x-menu-item-checked") ? String(n) : '';
49153         });
49154         this.setValue(val, true);
49155     },
49156
49157     /**
49158      * Sets the checked state of the checkbox.
49159      * On is always based on a string comparison between inputValue and the param.
49160      * @param {Boolean/String} value - the value to set 
49161      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49162      */
49163     setValue : function(v,suppressEvent){
49164         if (!this.el.dom) {
49165             return;
49166         }
49167         var old = this.el.dom.value ;
49168         this.el.dom.value = v;
49169         if (suppressEvent) {
49170             return ;
49171         }
49172          
49173         // update display..
49174         this.viewEl.select('img',true).each(function(e,i,n)  {
49175             
49176             var on = e.is(".x-menu-item-checked");
49177             var newv = v.indexOf(String(n)) > -1;
49178             if (on != newv) {
49179                 e.toggleClass('x-menu-item-checked');
49180             }
49181             
49182         });
49183         
49184         
49185         this.fireEvent('change', this, v, old);
49186         
49187         
49188     },
49189    
49190     // handle setting of hidden value by some other method!!?!?
49191     setFromHidden: function()
49192     {
49193         if(!this.el){
49194             return;
49195         }
49196         //console.log("SET FROM HIDDEN");
49197         //alert('setFrom hidden');
49198         this.setValue(this.el.dom.value);
49199     },
49200     
49201     onDestroy : function()
49202     {
49203         if(this.viewEl){
49204             Roo.get(this.viewEl).remove();
49205         }
49206          
49207         Roo.form.DayPicker.superclass.onDestroy.call(this);
49208     }
49209
49210 });/*
49211  * RooJS Library 1.1.1
49212  * Copyright(c) 2008-2011  Alan Knowles
49213  *
49214  * License - LGPL
49215  */
49216  
49217
49218 /**
49219  * @class Roo.form.ComboCheck
49220  * @extends Roo.form.ComboBox
49221  * A combobox for multiple select items.
49222  *
49223  * FIXME - could do with a reset button..
49224  * 
49225  * @constructor
49226  * Create a new ComboCheck
49227  * @param {Object} config Configuration options
49228  */
49229 Roo.form.ComboCheck = function(config){
49230     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49231     // should verify some data...
49232     // like
49233     // hiddenName = required..
49234     // displayField = required
49235     // valudField == required
49236     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49237     var _t = this;
49238     Roo.each(req, function(e) {
49239         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49240             throw "Roo.form.ComboCheck : missing value for: " + e;
49241         }
49242     });
49243     
49244     
49245 };
49246
49247 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49248      
49249      
49250     editable : false,
49251      
49252     selectedClass: 'x-menu-item-checked', 
49253     
49254     // private
49255     onRender : function(ct, position){
49256         var _t = this;
49257         
49258         
49259         
49260         if(!this.tpl){
49261             var cls = 'x-combo-list';
49262
49263             
49264             this.tpl =  new Roo.Template({
49265                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49266                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49267                    '<span>{' + this.displayField + '}</span>' +
49268                     '</div>' 
49269                 
49270             });
49271         }
49272  
49273         
49274         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49275         this.view.singleSelect = false;
49276         this.view.multiSelect = true;
49277         this.view.toggleSelect = true;
49278         this.pageTb.add(new Roo.Toolbar.Fill(), {
49279             
49280             text: 'Done',
49281             handler: function()
49282             {
49283                 _t.collapse();
49284             }
49285         });
49286     },
49287     
49288     onViewOver : function(e, t){
49289         // do nothing...
49290         return;
49291         
49292     },
49293     
49294     onViewClick : function(doFocus,index){
49295         return;
49296         
49297     },
49298     select: function () {
49299         //Roo.log("SELECT CALLED");
49300     },
49301      
49302     selectByValue : function(xv, scrollIntoView){
49303         var ar = this.getValueArray();
49304         var sels = [];
49305         
49306         Roo.each(ar, function(v) {
49307             if(v === undefined || v === null){
49308                 return;
49309             }
49310             var r = this.findRecord(this.valueField, v);
49311             if(r){
49312                 sels.push(this.store.indexOf(r))
49313                 
49314             }
49315         },this);
49316         this.view.select(sels);
49317         return false;
49318     },
49319     
49320     
49321     
49322     onSelect : function(record, index){
49323        // Roo.log("onselect Called");
49324        // this is only called by the clear button now..
49325         this.view.clearSelections();
49326         this.setValue('[]');
49327         if (this.value != this.valueBefore) {
49328             this.fireEvent('change', this, this.value, this.valueBefore);
49329             this.valueBefore = this.value;
49330         }
49331     },
49332     getValueArray : function()
49333     {
49334         var ar = [] ;
49335         
49336         try {
49337             //Roo.log(this.value);
49338             if (typeof(this.value) == 'undefined') {
49339                 return [];
49340             }
49341             var ar = Roo.decode(this.value);
49342             return  ar instanceof Array ? ar : []; //?? valid?
49343             
49344         } catch(e) {
49345             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49346             return [];
49347         }
49348          
49349     },
49350     expand : function ()
49351     {
49352         
49353         Roo.form.ComboCheck.superclass.expand.call(this);
49354         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49355         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49356         
49357
49358     },
49359     
49360     collapse : function(){
49361         Roo.form.ComboCheck.superclass.collapse.call(this);
49362         var sl = this.view.getSelectedIndexes();
49363         var st = this.store;
49364         var nv = [];
49365         var tv = [];
49366         var r;
49367         Roo.each(sl, function(i) {
49368             r = st.getAt(i);
49369             nv.push(r.get(this.valueField));
49370         },this);
49371         this.setValue(Roo.encode(nv));
49372         if (this.value != this.valueBefore) {
49373
49374             this.fireEvent('change', this, this.value, this.valueBefore);
49375             this.valueBefore = this.value;
49376         }
49377         
49378     },
49379     
49380     setValue : function(v){
49381         // Roo.log(v);
49382         this.value = v;
49383         
49384         var vals = this.getValueArray();
49385         var tv = [];
49386         Roo.each(vals, function(k) {
49387             var r = this.findRecord(this.valueField, k);
49388             if(r){
49389                 tv.push(r.data[this.displayField]);
49390             }else if(this.valueNotFoundText !== undefined){
49391                 tv.push( this.valueNotFoundText );
49392             }
49393         },this);
49394        // Roo.log(tv);
49395         
49396         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49397         this.hiddenField.value = v;
49398         this.value = v;
49399     }
49400     
49401 });/*
49402  * Based on:
49403  * Ext JS Library 1.1.1
49404  * Copyright(c) 2006-2007, Ext JS, LLC.
49405  *
49406  * Originally Released Under LGPL - original licence link has changed is not relivant.
49407  *
49408  * Fork - LGPL
49409  * <script type="text/javascript">
49410  */
49411  
49412 /**
49413  * @class Roo.form.Signature
49414  * @extends Roo.form.Field
49415  * Signature field.  
49416  * @constructor
49417  * 
49418  * @param {Object} config Configuration options
49419  */
49420
49421 Roo.form.Signature = function(config){
49422     Roo.form.Signature.superclass.constructor.call(this, config);
49423     
49424     this.addEvents({// not in used??
49425          /**
49426          * @event confirm
49427          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49428              * @param {Roo.form.Signature} combo This combo box
49429              */
49430         'confirm' : true,
49431         /**
49432          * @event reset
49433          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49434              * @param {Roo.form.ComboBox} combo This combo box
49435              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49436              */
49437         'reset' : true
49438     });
49439 };
49440
49441 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49442     /**
49443      * @cfg {Object} labels Label to use when rendering a form.
49444      * defaults to 
49445      * labels : { 
49446      *      clear : "Clear",
49447      *      confirm : "Confirm"
49448      *  }
49449      */
49450     labels : { 
49451         clear : "Clear",
49452         confirm : "Confirm"
49453     },
49454     /**
49455      * @cfg {Number} width The signature panel width (defaults to 300)
49456      */
49457     width: 300,
49458     /**
49459      * @cfg {Number} height The signature panel height (defaults to 100)
49460      */
49461     height : 100,
49462     /**
49463      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49464      */
49465     allowBlank : false,
49466     
49467     //private
49468     // {Object} signPanel The signature SVG panel element (defaults to {})
49469     signPanel : {},
49470     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49471     isMouseDown : false,
49472     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49473     isConfirmed : false,
49474     // {String} signatureTmp SVG mapping string (defaults to empty string)
49475     signatureTmp : '',
49476     
49477     
49478     defaultAutoCreate : { // modified by initCompnoent..
49479         tag: "input",
49480         type:"hidden"
49481     },
49482
49483     // private
49484     onRender : function(ct, position){
49485         
49486         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49487         
49488         this.wrap = this.el.wrap({
49489             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49490         });
49491         
49492         this.createToolbar(this);
49493         this.signPanel = this.wrap.createChild({
49494                 tag: 'div',
49495                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49496             }, this.el
49497         );
49498             
49499         this.svgID = Roo.id();
49500         this.svgEl = this.signPanel.createChild({
49501               xmlns : 'http://www.w3.org/2000/svg',
49502               tag : 'svg',
49503               id : this.svgID + "-svg",
49504               width: this.width,
49505               height: this.height,
49506               viewBox: '0 0 '+this.width+' '+this.height,
49507               cn : [
49508                 {
49509                     tag: "rect",
49510                     id: this.svgID + "-svg-r",
49511                     width: this.width,
49512                     height: this.height,
49513                     fill: "#ffa"
49514                 },
49515                 {
49516                     tag: "line",
49517                     id: this.svgID + "-svg-l",
49518                     x1: "0", // start
49519                     y1: (this.height*0.8), // start set the line in 80% of height
49520                     x2: this.width, // end
49521                     y2: (this.height*0.8), // end set the line in 80% of height
49522                     'stroke': "#666",
49523                     'stroke-width': "1",
49524                     'stroke-dasharray': "3",
49525                     'shape-rendering': "crispEdges",
49526                     'pointer-events': "none"
49527                 },
49528                 {
49529                     tag: "path",
49530                     id: this.svgID + "-svg-p",
49531                     'stroke': "navy",
49532                     'stroke-width': "3",
49533                     'fill': "none",
49534                     'pointer-events': 'none'
49535                 }
49536               ]
49537         });
49538         this.createSVG();
49539         this.svgBox = this.svgEl.dom.getScreenCTM();
49540     },
49541     createSVG : function(){ 
49542         var svg = this.signPanel;
49543         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49544         var t = this;
49545
49546         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49547         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49548         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49549         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49550         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49551         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49552         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49553         
49554     },
49555     isTouchEvent : function(e){
49556         return e.type.match(/^touch/);
49557     },
49558     getCoords : function (e) {
49559         var pt    = this.svgEl.dom.createSVGPoint();
49560         pt.x = e.clientX; 
49561         pt.y = e.clientY;
49562         if (this.isTouchEvent(e)) {
49563             pt.x =  e.targetTouches[0].clientX;
49564             pt.y = e.targetTouches[0].clientY;
49565         }
49566         var a = this.svgEl.dom.getScreenCTM();
49567         var b = a.inverse();
49568         var mx = pt.matrixTransform(b);
49569         return mx.x + ',' + mx.y;
49570     },
49571     //mouse event headler 
49572     down : function (e) {
49573         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49574         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49575         
49576         this.isMouseDown = true;
49577         
49578         e.preventDefault();
49579     },
49580     move : function (e) {
49581         if (this.isMouseDown) {
49582             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49583             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49584         }
49585         
49586         e.preventDefault();
49587     },
49588     up : function (e) {
49589         this.isMouseDown = false;
49590         var sp = this.signatureTmp.split(' ');
49591         
49592         if(sp.length > 1){
49593             if(!sp[sp.length-2].match(/^L/)){
49594                 sp.pop();
49595                 sp.pop();
49596                 sp.push("");
49597                 this.signatureTmp = sp.join(" ");
49598             }
49599         }
49600         if(this.getValue() != this.signatureTmp){
49601             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49602             this.isConfirmed = false;
49603         }
49604         e.preventDefault();
49605     },
49606     
49607     /**
49608      * Protected method that will not generally be called directly. It
49609      * is called when the editor creates its toolbar. Override this method if you need to
49610      * add custom toolbar buttons.
49611      * @param {HtmlEditor} editor
49612      */
49613     createToolbar : function(editor){
49614          function btn(id, toggle, handler){
49615             var xid = fid + '-'+ id ;
49616             return {
49617                 id : xid,
49618                 cmd : id,
49619                 cls : 'x-btn-icon x-edit-'+id,
49620                 enableToggle:toggle !== false,
49621                 scope: editor, // was editor...
49622                 handler:handler||editor.relayBtnCmd,
49623                 clickEvent:'mousedown',
49624                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49625                 tabIndex:-1
49626             };
49627         }
49628         
49629         
49630         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49631         this.tb = tb;
49632         this.tb.add(
49633            {
49634                 cls : ' x-signature-btn x-signature-'+id,
49635                 scope: editor, // was editor...
49636                 handler: this.reset,
49637                 clickEvent:'mousedown',
49638                 text: this.labels.clear
49639             },
49640             {
49641                  xtype : 'Fill',
49642                  xns: Roo.Toolbar
49643             }, 
49644             {
49645                 cls : '  x-signature-btn x-signature-'+id,
49646                 scope: editor, // was editor...
49647                 handler: this.confirmHandler,
49648                 clickEvent:'mousedown',
49649                 text: this.labels.confirm
49650             }
49651         );
49652     
49653     },
49654     //public
49655     /**
49656      * when user is clicked confirm then show this image.....
49657      * 
49658      * @return {String} Image Data URI
49659      */
49660     getImageDataURI : function(){
49661         var svg = this.svgEl.dom.parentNode.innerHTML;
49662         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49663         return src; 
49664     },
49665     /**
49666      * 
49667      * @return {Boolean} this.isConfirmed
49668      */
49669     getConfirmed : function(){
49670         return this.isConfirmed;
49671     },
49672     /**
49673      * 
49674      * @return {Number} this.width
49675      */
49676     getWidth : function(){
49677         return this.width;
49678     },
49679     /**
49680      * 
49681      * @return {Number} this.height
49682      */
49683     getHeight : function(){
49684         return this.height;
49685     },
49686     // private
49687     getSignature : function(){
49688         return this.signatureTmp;
49689     },
49690     // private
49691     reset : function(){
49692         this.signatureTmp = '';
49693         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49694         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49695         this.isConfirmed = false;
49696         Roo.form.Signature.superclass.reset.call(this);
49697     },
49698     setSignature : function(s){
49699         this.signatureTmp = s;
49700         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49701         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49702         this.setValue(s);
49703         this.isConfirmed = false;
49704         Roo.form.Signature.superclass.reset.call(this);
49705     }, 
49706     test : function(){
49707 //        Roo.log(this.signPanel.dom.contentWindow.up())
49708     },
49709     //private
49710     setConfirmed : function(){
49711         
49712         
49713         
49714 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49715     },
49716     // private
49717     confirmHandler : function(){
49718         if(!this.getSignature()){
49719             return;
49720         }
49721         
49722         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49723         this.setValue(this.getSignature());
49724         this.isConfirmed = true;
49725         
49726         this.fireEvent('confirm', this);
49727     },
49728     // private
49729     // Subclasses should provide the validation implementation by overriding this
49730     validateValue : function(value){
49731         if(this.allowBlank){
49732             return true;
49733         }
49734         
49735         if(this.isConfirmed){
49736             return true;
49737         }
49738         return false;
49739     }
49740 });/*
49741  * Based on:
49742  * Ext JS Library 1.1.1
49743  * Copyright(c) 2006-2007, Ext JS, LLC.
49744  *
49745  * Originally Released Under LGPL - original licence link has changed is not relivant.
49746  *
49747  * Fork - LGPL
49748  * <script type="text/javascript">
49749  */
49750  
49751
49752 /**
49753  * @class Roo.form.ComboBox
49754  * @extends Roo.form.TriggerField
49755  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49756  * @constructor
49757  * Create a new ComboBox.
49758  * @param {Object} config Configuration options
49759  */
49760 Roo.form.Select = function(config){
49761     Roo.form.Select.superclass.constructor.call(this, config);
49762      
49763 };
49764
49765 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49766     /**
49767      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49768      */
49769     /**
49770      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49771      * rendering into an Roo.Editor, defaults to false)
49772      */
49773     /**
49774      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49775      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49776      */
49777     /**
49778      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49779      */
49780     /**
49781      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49782      * the dropdown list (defaults to undefined, with no header element)
49783      */
49784
49785      /**
49786      * @cfg {String/Roo.Template} tpl The template to use to render the output
49787      */
49788      
49789     // private
49790     defaultAutoCreate : {tag: "select"  },
49791     /**
49792      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49793      */
49794     listWidth: undefined,
49795     /**
49796      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49797      * mode = 'remote' or 'text' if mode = 'local')
49798      */
49799     displayField: undefined,
49800     /**
49801      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49802      * mode = 'remote' or 'value' if mode = 'local'). 
49803      * Note: use of a valueField requires the user make a selection
49804      * in order for a value to be mapped.
49805      */
49806     valueField: undefined,
49807     
49808     
49809     /**
49810      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49811      * field's data value (defaults to the underlying DOM element's name)
49812      */
49813     hiddenName: undefined,
49814     /**
49815      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49816      */
49817     listClass: '',
49818     /**
49819      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49820      */
49821     selectedClass: 'x-combo-selected',
49822     /**
49823      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49824      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49825      * which displays a downward arrow icon).
49826      */
49827     triggerClass : 'x-form-arrow-trigger',
49828     /**
49829      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49830      */
49831     shadow:'sides',
49832     /**
49833      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49834      * anchor positions (defaults to 'tl-bl')
49835      */
49836     listAlign: 'tl-bl?',
49837     /**
49838      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49839      */
49840     maxHeight: 300,
49841     /**
49842      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49843      * query specified by the allQuery config option (defaults to 'query')
49844      */
49845     triggerAction: 'query',
49846     /**
49847      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49848      * (defaults to 4, does not apply if editable = false)
49849      */
49850     minChars : 4,
49851     /**
49852      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49853      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49854      */
49855     typeAhead: false,
49856     /**
49857      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49858      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49859      */
49860     queryDelay: 500,
49861     /**
49862      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49863      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49864      */
49865     pageSize: 0,
49866     /**
49867      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49868      * when editable = true (defaults to false)
49869      */
49870     selectOnFocus:false,
49871     /**
49872      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49873      */
49874     queryParam: 'query',
49875     /**
49876      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49877      * when mode = 'remote' (defaults to 'Loading...')
49878      */
49879     loadingText: 'Loading...',
49880     /**
49881      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49882      */
49883     resizable: false,
49884     /**
49885      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49886      */
49887     handleHeight : 8,
49888     /**
49889      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49890      * traditional select (defaults to true)
49891      */
49892     editable: true,
49893     /**
49894      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49895      */
49896     allQuery: '',
49897     /**
49898      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49899      */
49900     mode: 'remote',
49901     /**
49902      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49903      * listWidth has a higher value)
49904      */
49905     minListWidth : 70,
49906     /**
49907      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49908      * allow the user to set arbitrary text into the field (defaults to false)
49909      */
49910     forceSelection:false,
49911     /**
49912      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49913      * if typeAhead = true (defaults to 250)
49914      */
49915     typeAheadDelay : 250,
49916     /**
49917      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49918      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49919      */
49920     valueNotFoundText : undefined,
49921     
49922     /**
49923      * @cfg {String} defaultValue The value displayed after loading the store.
49924      */
49925     defaultValue: '',
49926     
49927     /**
49928      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49929      */
49930     blockFocus : false,
49931     
49932     /**
49933      * @cfg {Boolean} disableClear Disable showing of clear button.
49934      */
49935     disableClear : false,
49936     /**
49937      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49938      */
49939     alwaysQuery : false,
49940     
49941     //private
49942     addicon : false,
49943     editicon: false,
49944     
49945     // element that contains real text value.. (when hidden is used..)
49946      
49947     // private
49948     onRender : function(ct, position){
49949         Roo.form.Field.prototype.onRender.call(this, ct, position);
49950         
49951         if(this.store){
49952             this.store.on('beforeload', this.onBeforeLoad, this);
49953             this.store.on('load', this.onLoad, this);
49954             this.store.on('loadexception', this.onLoadException, this);
49955             this.store.load({});
49956         }
49957         
49958         
49959         
49960     },
49961
49962     // private
49963     initEvents : function(){
49964         //Roo.form.ComboBox.superclass.initEvents.call(this);
49965  
49966     },
49967
49968     onDestroy : function(){
49969        
49970         if(this.store){
49971             this.store.un('beforeload', this.onBeforeLoad, this);
49972             this.store.un('load', this.onLoad, this);
49973             this.store.un('loadexception', this.onLoadException, this);
49974         }
49975         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49976     },
49977
49978     // private
49979     fireKey : function(e){
49980         if(e.isNavKeyPress() && !this.list.isVisible()){
49981             this.fireEvent("specialkey", this, e);
49982         }
49983     },
49984
49985     // private
49986     onResize: function(w, h){
49987         
49988         return; 
49989     
49990         
49991     },
49992
49993     /**
49994      * Allow or prevent the user from directly editing the field text.  If false is passed,
49995      * the user will only be able to select from the items defined in the dropdown list.  This method
49996      * is the runtime equivalent of setting the 'editable' config option at config time.
49997      * @param {Boolean} value True to allow the user to directly edit the field text
49998      */
49999     setEditable : function(value){
50000          
50001     },
50002
50003     // private
50004     onBeforeLoad : function(){
50005         
50006         Roo.log("Select before load");
50007         return;
50008     
50009         this.innerList.update(this.loadingText ?
50010                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50011         //this.restrictHeight();
50012         this.selectedIndex = -1;
50013     },
50014
50015     // private
50016     onLoad : function(){
50017
50018     
50019         var dom = this.el.dom;
50020         dom.innerHTML = '';
50021          var od = dom.ownerDocument;
50022          
50023         if (this.emptyText) {
50024             var op = od.createElement('option');
50025             op.setAttribute('value', '');
50026             op.innerHTML = String.format('{0}', this.emptyText);
50027             dom.appendChild(op);
50028         }
50029         if(this.store.getCount() > 0){
50030            
50031             var vf = this.valueField;
50032             var df = this.displayField;
50033             this.store.data.each(function(r) {
50034                 // which colmsn to use... testing - cdoe / title..
50035                 var op = od.createElement('option');
50036                 op.setAttribute('value', r.data[vf]);
50037                 op.innerHTML = String.format('{0}', r.data[df]);
50038                 dom.appendChild(op);
50039             });
50040             if (typeof(this.defaultValue != 'undefined')) {
50041                 this.setValue(this.defaultValue);
50042             }
50043             
50044              
50045         }else{
50046             //this.onEmptyResults();
50047         }
50048         //this.el.focus();
50049     },
50050     // private
50051     onLoadException : function()
50052     {
50053         dom.innerHTML = '';
50054             
50055         Roo.log("Select on load exception");
50056         return;
50057     
50058         this.collapse();
50059         Roo.log(this.store.reader.jsonData);
50060         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50061             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50062         }
50063         
50064         
50065     },
50066     // private
50067     onTypeAhead : function(){
50068          
50069     },
50070
50071     // private
50072     onSelect : function(record, index){
50073         Roo.log('on select?');
50074         return;
50075         if(this.fireEvent('beforeselect', this, record, index) !== false){
50076             this.setFromData(index > -1 ? record.data : false);
50077             this.collapse();
50078             this.fireEvent('select', this, record, index);
50079         }
50080     },
50081
50082     /**
50083      * Returns the currently selected field value or empty string if no value is set.
50084      * @return {String} value The selected value
50085      */
50086     getValue : function(){
50087         var dom = this.el.dom;
50088         this.value = dom.options[dom.selectedIndex].value;
50089         return this.value;
50090         
50091     },
50092
50093     /**
50094      * Clears any text/value currently set in the field
50095      */
50096     clearValue : function(){
50097         this.value = '';
50098         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50099         
50100     },
50101
50102     /**
50103      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50104      * will be displayed in the field.  If the value does not match the data value of an existing item,
50105      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50106      * Otherwise the field will be blank (although the value will still be set).
50107      * @param {String} value The value to match
50108      */
50109     setValue : function(v){
50110         var d = this.el.dom;
50111         for (var i =0; i < d.options.length;i++) {
50112             if (v == d.options[i].value) {
50113                 d.selectedIndex = i;
50114                 this.value = v;
50115                 return;
50116             }
50117         }
50118         this.clearValue();
50119     },
50120     /**
50121      * @property {Object} the last set data for the element
50122      */
50123     
50124     lastData : false,
50125     /**
50126      * Sets the value of the field based on a object which is related to the record format for the store.
50127      * @param {Object} value the value to set as. or false on reset?
50128      */
50129     setFromData : function(o){
50130         Roo.log('setfrom data?');
50131          
50132         
50133         
50134     },
50135     // private
50136     reset : function(){
50137         this.clearValue();
50138     },
50139     // private
50140     findRecord : function(prop, value){
50141         
50142         return false;
50143     
50144         var record;
50145         if(this.store.getCount() > 0){
50146             this.store.each(function(r){
50147                 if(r.data[prop] == value){
50148                     record = r;
50149                     return false;
50150                 }
50151                 return true;
50152             });
50153         }
50154         return record;
50155     },
50156     
50157     getName: function()
50158     {
50159         // returns hidden if it's set..
50160         if (!this.rendered) {return ''};
50161         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50162         
50163     },
50164      
50165
50166     
50167
50168     // private
50169     onEmptyResults : function(){
50170         Roo.log('empty results');
50171         //this.collapse();
50172     },
50173
50174     /**
50175      * Returns true if the dropdown list is expanded, else false.
50176      */
50177     isExpanded : function(){
50178         return false;
50179     },
50180
50181     /**
50182      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50183      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50184      * @param {String} value The data value of the item to select
50185      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50186      * selected item if it is not currently in view (defaults to true)
50187      * @return {Boolean} True if the value matched an item in the list, else false
50188      */
50189     selectByValue : function(v, scrollIntoView){
50190         Roo.log('select By Value');
50191         return false;
50192     
50193         if(v !== undefined && v !== null){
50194             var r = this.findRecord(this.valueField || this.displayField, v);
50195             if(r){
50196                 this.select(this.store.indexOf(r), scrollIntoView);
50197                 return true;
50198             }
50199         }
50200         return false;
50201     },
50202
50203     /**
50204      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50205      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50206      * @param {Number} index The zero-based index of the list item to select
50207      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50208      * selected item if it is not currently in view (defaults to true)
50209      */
50210     select : function(index, scrollIntoView){
50211         Roo.log('select ');
50212         return  ;
50213         
50214         this.selectedIndex = index;
50215         this.view.select(index);
50216         if(scrollIntoView !== false){
50217             var el = this.view.getNode(index);
50218             if(el){
50219                 this.innerList.scrollChildIntoView(el, false);
50220             }
50221         }
50222     },
50223
50224       
50225
50226     // private
50227     validateBlur : function(){
50228         
50229         return;
50230         
50231     },
50232
50233     // private
50234     initQuery : function(){
50235         this.doQuery(this.getRawValue());
50236     },
50237
50238     // private
50239     doForce : function(){
50240         if(this.el.dom.value.length > 0){
50241             this.el.dom.value =
50242                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50243              
50244         }
50245     },
50246
50247     /**
50248      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50249      * query allowing the query action to be canceled if needed.
50250      * @param {String} query The SQL query to execute
50251      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50252      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50253      * saved in the current store (defaults to false)
50254      */
50255     doQuery : function(q, forceAll){
50256         
50257         Roo.log('doQuery?');
50258         if(q === undefined || q === null){
50259             q = '';
50260         }
50261         var qe = {
50262             query: q,
50263             forceAll: forceAll,
50264             combo: this,
50265             cancel:false
50266         };
50267         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50268             return false;
50269         }
50270         q = qe.query;
50271         forceAll = qe.forceAll;
50272         if(forceAll === true || (q.length >= this.minChars)){
50273             if(this.lastQuery != q || this.alwaysQuery){
50274                 this.lastQuery = q;
50275                 if(this.mode == 'local'){
50276                     this.selectedIndex = -1;
50277                     if(forceAll){
50278                         this.store.clearFilter();
50279                     }else{
50280                         this.store.filter(this.displayField, q);
50281                     }
50282                     this.onLoad();
50283                 }else{
50284                     this.store.baseParams[this.queryParam] = q;
50285                     this.store.load({
50286                         params: this.getParams(q)
50287                     });
50288                     this.expand();
50289                 }
50290             }else{
50291                 this.selectedIndex = -1;
50292                 this.onLoad();   
50293             }
50294         }
50295     },
50296
50297     // private
50298     getParams : function(q){
50299         var p = {};
50300         //p[this.queryParam] = q;
50301         if(this.pageSize){
50302             p.start = 0;
50303             p.limit = this.pageSize;
50304         }
50305         return p;
50306     },
50307
50308     /**
50309      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50310      */
50311     collapse : function(){
50312         
50313     },
50314
50315     // private
50316     collapseIf : function(e){
50317         
50318     },
50319
50320     /**
50321      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50322      */
50323     expand : function(){
50324         
50325     } ,
50326
50327     // private
50328      
50329
50330     /** 
50331     * @cfg {Boolean} grow 
50332     * @hide 
50333     */
50334     /** 
50335     * @cfg {Number} growMin 
50336     * @hide 
50337     */
50338     /** 
50339     * @cfg {Number} growMax 
50340     * @hide 
50341     */
50342     /**
50343      * @hide
50344      * @method autoSize
50345      */
50346     
50347     setWidth : function()
50348     {
50349         
50350     },
50351     getResizeEl : function(){
50352         return this.el;
50353     }
50354 });//<script type="text/javasscript">
50355  
50356
50357 /**
50358  * @class Roo.DDView
50359  * A DnD enabled version of Roo.View.
50360  * @param {Element/String} container The Element in which to create the View.
50361  * @param {String} tpl The template string used to create the markup for each element of the View
50362  * @param {Object} config The configuration properties. These include all the config options of
50363  * {@link Roo.View} plus some specific to this class.<br>
50364  * <p>
50365  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50366  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50367  * <p>
50368  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50369 .x-view-drag-insert-above {
50370         border-top:1px dotted #3366cc;
50371 }
50372 .x-view-drag-insert-below {
50373         border-bottom:1px dotted #3366cc;
50374 }
50375 </code></pre>
50376  * 
50377  */
50378  
50379 Roo.DDView = function(container, tpl, config) {
50380     Roo.DDView.superclass.constructor.apply(this, arguments);
50381     this.getEl().setStyle("outline", "0px none");
50382     this.getEl().unselectable();
50383     if (this.dragGroup) {
50384                 this.setDraggable(this.dragGroup.split(","));
50385     }
50386     if (this.dropGroup) {
50387                 this.setDroppable(this.dropGroup.split(","));
50388     }
50389     if (this.deletable) {
50390         this.setDeletable();
50391     }
50392     this.isDirtyFlag = false;
50393         this.addEvents({
50394                 "drop" : true
50395         });
50396 };
50397
50398 Roo.extend(Roo.DDView, Roo.View, {
50399 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50400 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50401 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50402 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50403
50404         isFormField: true,
50405
50406         reset: Roo.emptyFn,
50407         
50408         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50409
50410         validate: function() {
50411                 return true;
50412         },
50413         
50414         destroy: function() {
50415                 this.purgeListeners();
50416                 this.getEl.removeAllListeners();
50417                 this.getEl().remove();
50418                 if (this.dragZone) {
50419                         if (this.dragZone.destroy) {
50420                                 this.dragZone.destroy();
50421                         }
50422                 }
50423                 if (this.dropZone) {
50424                         if (this.dropZone.destroy) {
50425                                 this.dropZone.destroy();
50426                         }
50427                 }
50428         },
50429
50430 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50431         getName: function() {
50432                 return this.name;
50433         },
50434
50435 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50436         setValue: function(v) {
50437                 if (!this.store) {
50438                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50439                 }
50440                 var data = {};
50441                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50442                 this.store.proxy = new Roo.data.MemoryProxy(data);
50443                 this.store.load();
50444         },
50445
50446 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50447         getValue: function() {
50448                 var result = '(';
50449                 this.store.each(function(rec) {
50450                         result += rec.id + ',';
50451                 });
50452                 return result.substr(0, result.length - 1) + ')';
50453         },
50454         
50455         getIds: function() {
50456                 var i = 0, result = new Array(this.store.getCount());
50457                 this.store.each(function(rec) {
50458                         result[i++] = rec.id;
50459                 });
50460                 return result;
50461         },
50462         
50463         isDirty: function() {
50464                 return this.isDirtyFlag;
50465         },
50466
50467 /**
50468  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50469  *      whole Element becomes the target, and this causes the drop gesture to append.
50470  */
50471     getTargetFromEvent : function(e) {
50472                 var target = e.getTarget();
50473                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50474                 target = target.parentNode;
50475                 }
50476                 if (!target) {
50477                         target = this.el.dom.lastChild || this.el.dom;
50478                 }
50479                 return target;
50480     },
50481
50482 /**
50483  *      Create the drag data which consists of an object which has the property "ddel" as
50484  *      the drag proxy element. 
50485  */
50486     getDragData : function(e) {
50487         var target = this.findItemFromChild(e.getTarget());
50488                 if(target) {
50489                         this.handleSelection(e);
50490                         var selNodes = this.getSelectedNodes();
50491             var dragData = {
50492                 source: this,
50493                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50494                 nodes: selNodes,
50495                 records: []
50496                         };
50497                         var selectedIndices = this.getSelectedIndexes();
50498                         for (var i = 0; i < selectedIndices.length; i++) {
50499                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50500                         }
50501                         if (selNodes.length == 1) {
50502                                 dragData.ddel = target.cloneNode(true); // the div element
50503                         } else {
50504                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50505                                 div.className = 'multi-proxy';
50506                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50507                                         div.appendChild(selNodes[i].cloneNode(true));
50508                                 }
50509                                 dragData.ddel = div;
50510                         }
50511             //console.log(dragData)
50512             //console.log(dragData.ddel.innerHTML)
50513                         return dragData;
50514                 }
50515         //console.log('nodragData')
50516                 return false;
50517     },
50518     
50519 /**     Specify to which ddGroup items in this DDView may be dragged. */
50520     setDraggable: function(ddGroup) {
50521         if (ddGroup instanceof Array) {
50522                 Roo.each(ddGroup, this.setDraggable, this);
50523                 return;
50524         }
50525         if (this.dragZone) {
50526                 this.dragZone.addToGroup(ddGroup);
50527         } else {
50528                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50529                                 containerScroll: true,
50530                                 ddGroup: ddGroup 
50531
50532                         });
50533 //                      Draggability implies selection. DragZone's mousedown selects the element.
50534                         if (!this.multiSelect) { this.singleSelect = true; }
50535
50536 //                      Wire the DragZone's handlers up to methods in *this*
50537                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50538                 }
50539     },
50540
50541 /**     Specify from which ddGroup this DDView accepts drops. */
50542     setDroppable: function(ddGroup) {
50543         if (ddGroup instanceof Array) {
50544                 Roo.each(ddGroup, this.setDroppable, this);
50545                 return;
50546         }
50547         if (this.dropZone) {
50548                 this.dropZone.addToGroup(ddGroup);
50549         } else {
50550                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50551                                 containerScroll: true,
50552                                 ddGroup: ddGroup
50553                         });
50554
50555 //                      Wire the DropZone's handlers up to methods in *this*
50556                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50557                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50558                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50559                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50560                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50561                 }
50562     },
50563
50564 /**     Decide whether to drop above or below a View node. */
50565     getDropPoint : function(e, n, dd){
50566         if (n == this.el.dom) { return "above"; }
50567                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50568                 var c = t + (b - t) / 2;
50569                 var y = Roo.lib.Event.getPageY(e);
50570                 if(y <= c) {
50571                         return "above";
50572                 }else{
50573                         return "below";
50574                 }
50575     },
50576
50577     onNodeEnter : function(n, dd, e, data){
50578                 return false;
50579     },
50580     
50581     onNodeOver : function(n, dd, e, data){
50582                 var pt = this.getDropPoint(e, n, dd);
50583                 // set the insert point style on the target node
50584                 var dragElClass = this.dropNotAllowed;
50585                 if (pt) {
50586                         var targetElClass;
50587                         if (pt == "above"){
50588                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50589                                 targetElClass = "x-view-drag-insert-above";
50590                         } else {
50591                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50592                                 targetElClass = "x-view-drag-insert-below";
50593                         }
50594                         if (this.lastInsertClass != targetElClass){
50595                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50596                                 this.lastInsertClass = targetElClass;
50597                         }
50598                 }
50599                 return dragElClass;
50600         },
50601
50602     onNodeOut : function(n, dd, e, data){
50603                 this.removeDropIndicators(n);
50604     },
50605
50606     onNodeDrop : function(n, dd, e, data){
50607         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50608                 return false;
50609         }
50610         var pt = this.getDropPoint(e, n, dd);
50611                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50612                 if (pt == "below") { insertAt++; }
50613                 for (var i = 0; i < data.records.length; i++) {
50614                         var r = data.records[i];
50615                         var dup = this.store.getById(r.id);
50616                         if (dup && (dd != this.dragZone)) {
50617                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50618                         } else {
50619                                 if (data.copy) {
50620                                         this.store.insert(insertAt++, r.copy());
50621                                 } else {
50622                                         data.source.isDirtyFlag = true;
50623                                         r.store.remove(r);
50624                                         this.store.insert(insertAt++, r);
50625                                 }
50626                                 this.isDirtyFlag = true;
50627                         }
50628                 }
50629                 this.dragZone.cachedTarget = null;
50630                 return true;
50631     },
50632
50633     removeDropIndicators : function(n){
50634                 if(n){
50635                         Roo.fly(n).removeClass([
50636                                 "x-view-drag-insert-above",
50637                                 "x-view-drag-insert-below"]);
50638                         this.lastInsertClass = "_noclass";
50639                 }
50640     },
50641
50642 /**
50643  *      Utility method. Add a delete option to the DDView's context menu.
50644  *      @param {String} imageUrl The URL of the "delete" icon image.
50645  */
50646         setDeletable: function(imageUrl) {
50647                 if (!this.singleSelect && !this.multiSelect) {
50648                         this.singleSelect = true;
50649                 }
50650                 var c = this.getContextMenu();
50651                 this.contextMenu.on("itemclick", function(item) {
50652                         switch (item.id) {
50653                                 case "delete":
50654                                         this.remove(this.getSelectedIndexes());
50655                                         break;
50656                         }
50657                 }, this);
50658                 this.contextMenu.add({
50659                         icon: imageUrl,
50660                         id: "delete",
50661                         text: 'Delete'
50662                 });
50663         },
50664         
50665 /**     Return the context menu for this DDView. */
50666         getContextMenu: function() {
50667                 if (!this.contextMenu) {
50668 //                      Create the View's context menu
50669                         this.contextMenu = new Roo.menu.Menu({
50670                                 id: this.id + "-contextmenu"
50671                         });
50672                         this.el.on("contextmenu", this.showContextMenu, this);
50673                 }
50674                 return this.contextMenu;
50675         },
50676         
50677         disableContextMenu: function() {
50678                 if (this.contextMenu) {
50679                         this.el.un("contextmenu", this.showContextMenu, this);
50680                 }
50681         },
50682
50683         showContextMenu: function(e, item) {
50684         item = this.findItemFromChild(e.getTarget());
50685                 if (item) {
50686                         e.stopEvent();
50687                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50688                         this.contextMenu.showAt(e.getXY());
50689             }
50690     },
50691
50692 /**
50693  *      Remove {@link Roo.data.Record}s at the specified indices.
50694  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50695  */
50696     remove: function(selectedIndices) {
50697                 selectedIndices = [].concat(selectedIndices);
50698                 for (var i = 0; i < selectedIndices.length; i++) {
50699                         var rec = this.store.getAt(selectedIndices[i]);
50700                         this.store.remove(rec);
50701                 }
50702     },
50703
50704 /**
50705  *      Double click fires the event, but also, if this is draggable, and there is only one other
50706  *      related DropZone, it transfers the selected node.
50707  */
50708     onDblClick : function(e){
50709         var item = this.findItemFromChild(e.getTarget());
50710         if(item){
50711             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50712                 return false;
50713             }
50714             if (this.dragGroup) {
50715                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50716                     while (targets.indexOf(this.dropZone) > -1) {
50717                             targets.remove(this.dropZone);
50718                                 }
50719                     if (targets.length == 1) {
50720                                         this.dragZone.cachedTarget = null;
50721                         var el = Roo.get(targets[0].getEl());
50722                         var box = el.getBox(true);
50723                         targets[0].onNodeDrop(el.dom, {
50724                                 target: el.dom,
50725                                 xy: [box.x, box.y + box.height - 1]
50726                         }, null, this.getDragData(e));
50727                     }
50728                 }
50729         }
50730     },
50731     
50732     handleSelection: function(e) {
50733                 this.dragZone.cachedTarget = null;
50734         var item = this.findItemFromChild(e.getTarget());
50735         if (!item) {
50736                 this.clearSelections(true);
50737                 return;
50738         }
50739                 if (item && (this.multiSelect || this.singleSelect)){
50740                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50741                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50742                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50743                                 this.unselect(item);
50744                         } else {
50745                                 this.select(item, this.multiSelect && e.ctrlKey);
50746                                 this.lastSelection = item;
50747                         }
50748                 }
50749     },
50750
50751     onItemClick : function(item, index, e){
50752                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50753                         return false;
50754                 }
50755                 return true;
50756     },
50757
50758     unselect : function(nodeInfo, suppressEvent){
50759                 var node = this.getNode(nodeInfo);
50760                 if(node && this.isSelected(node)){
50761                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50762                                 Roo.fly(node).removeClass(this.selectedClass);
50763                                 this.selections.remove(node);
50764                                 if(!suppressEvent){
50765                                         this.fireEvent("selectionchange", this, this.selections);
50766                                 }
50767                         }
50768                 }
50769     }
50770 });
50771 /*
50772  * Based on:
50773  * Ext JS Library 1.1.1
50774  * Copyright(c) 2006-2007, Ext JS, LLC.
50775  *
50776  * Originally Released Under LGPL - original licence link has changed is not relivant.
50777  *
50778  * Fork - LGPL
50779  * <script type="text/javascript">
50780  */
50781  
50782 /**
50783  * @class Roo.LayoutManager
50784  * @extends Roo.util.Observable
50785  * Base class for layout managers.
50786  */
50787 Roo.LayoutManager = function(container, config){
50788     Roo.LayoutManager.superclass.constructor.call(this);
50789     this.el = Roo.get(container);
50790     // ie scrollbar fix
50791     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50792         document.body.scroll = "no";
50793     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50794         this.el.position('relative');
50795     }
50796     this.id = this.el.id;
50797     this.el.addClass("x-layout-container");
50798     /** false to disable window resize monitoring @type Boolean */
50799     this.monitorWindowResize = true;
50800     this.regions = {};
50801     this.addEvents({
50802         /**
50803          * @event layout
50804          * Fires when a layout is performed. 
50805          * @param {Roo.LayoutManager} this
50806          */
50807         "layout" : true,
50808         /**
50809          * @event regionresized
50810          * Fires when the user resizes a region. 
50811          * @param {Roo.LayoutRegion} region The resized region
50812          * @param {Number} newSize The new size (width for east/west, height for north/south)
50813          */
50814         "regionresized" : true,
50815         /**
50816          * @event regioncollapsed
50817          * Fires when a region is collapsed. 
50818          * @param {Roo.LayoutRegion} region The collapsed region
50819          */
50820         "regioncollapsed" : true,
50821         /**
50822          * @event regionexpanded
50823          * Fires when a region is expanded.  
50824          * @param {Roo.LayoutRegion} region The expanded region
50825          */
50826         "regionexpanded" : true
50827     });
50828     this.updating = false;
50829     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50830 };
50831
50832 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50833     /**
50834      * Returns true if this layout is currently being updated
50835      * @return {Boolean}
50836      */
50837     isUpdating : function(){
50838         return this.updating; 
50839     },
50840     
50841     /**
50842      * Suspend the LayoutManager from doing auto-layouts while
50843      * making multiple add or remove calls
50844      */
50845     beginUpdate : function(){
50846         this.updating = true;    
50847     },
50848     
50849     /**
50850      * Restore auto-layouts and optionally disable the manager from performing a layout
50851      * @param {Boolean} noLayout true to disable a layout update 
50852      */
50853     endUpdate : function(noLayout){
50854         this.updating = false;
50855         if(!noLayout){
50856             this.layout();
50857         }    
50858     },
50859     
50860     layout: function(){
50861         
50862     },
50863     
50864     onRegionResized : function(region, newSize){
50865         this.fireEvent("regionresized", region, newSize);
50866         this.layout();
50867     },
50868     
50869     onRegionCollapsed : function(region){
50870         this.fireEvent("regioncollapsed", region);
50871     },
50872     
50873     onRegionExpanded : function(region){
50874         this.fireEvent("regionexpanded", region);
50875     },
50876         
50877     /**
50878      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50879      * performs box-model adjustments.
50880      * @return {Object} The size as an object {width: (the width), height: (the height)}
50881      */
50882     getViewSize : function(){
50883         var size;
50884         if(this.el.dom != document.body){
50885             size = this.el.getSize();
50886         }else{
50887             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50888         }
50889         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50890         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50891         return size;
50892     },
50893     
50894     /**
50895      * Returns the Element this layout is bound to.
50896      * @return {Roo.Element}
50897      */
50898     getEl : function(){
50899         return this.el;
50900     },
50901     
50902     /**
50903      * Returns the specified region.
50904      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50905      * @return {Roo.LayoutRegion}
50906      */
50907     getRegion : function(target){
50908         return this.regions[target.toLowerCase()];
50909     },
50910     
50911     onWindowResize : function(){
50912         if(this.monitorWindowResize){
50913             this.layout();
50914         }
50915     }
50916 });/*
50917  * Based on:
50918  * Ext JS Library 1.1.1
50919  * Copyright(c) 2006-2007, Ext JS, LLC.
50920  *
50921  * Originally Released Under LGPL - original licence link has changed is not relivant.
50922  *
50923  * Fork - LGPL
50924  * <script type="text/javascript">
50925  */
50926 /**
50927  * @class Roo.BorderLayout
50928  * @extends Roo.LayoutManager
50929  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50930  * please see: <br><br>
50931  * <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>
50932  * <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>
50933  * Example:
50934  <pre><code>
50935  var layout = new Roo.BorderLayout(document.body, {
50936     north: {
50937         initialSize: 25,
50938         titlebar: false
50939     },
50940     west: {
50941         split:true,
50942         initialSize: 200,
50943         minSize: 175,
50944         maxSize: 400,
50945         titlebar: true,
50946         collapsible: true
50947     },
50948     east: {
50949         split:true,
50950         initialSize: 202,
50951         minSize: 175,
50952         maxSize: 400,
50953         titlebar: true,
50954         collapsible: true
50955     },
50956     south: {
50957         split:true,
50958         initialSize: 100,
50959         minSize: 100,
50960         maxSize: 200,
50961         titlebar: true,
50962         collapsible: true
50963     },
50964     center: {
50965         titlebar: true,
50966         autoScroll:true,
50967         resizeTabs: true,
50968         minTabWidth: 50,
50969         preferredTabWidth: 150
50970     }
50971 });
50972
50973 // shorthand
50974 var CP = Roo.ContentPanel;
50975
50976 layout.beginUpdate();
50977 layout.add("north", new CP("north", "North"));
50978 layout.add("south", new CP("south", {title: "South", closable: true}));
50979 layout.add("west", new CP("west", {title: "West"}));
50980 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50981 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50982 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50983 layout.getRegion("center").showPanel("center1");
50984 layout.endUpdate();
50985 </code></pre>
50986
50987 <b>The container the layout is rendered into can be either the body element or any other element.
50988 If it is not the body element, the container needs to either be an absolute positioned element,
50989 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50990 the container size if it is not the body element.</b>
50991
50992 * @constructor
50993 * Create a new BorderLayout
50994 * @param {String/HTMLElement/Element} container The container this layout is bound to
50995 * @param {Object} config Configuration options
50996  */
50997 Roo.BorderLayout = function(container, config){
50998     config = config || {};
50999     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51000     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51001     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51002         var target = this.factory.validRegions[i];
51003         if(config[target]){
51004             this.addRegion(target, config[target]);
51005         }
51006     }
51007 };
51008
51009 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51010     /**
51011      * Creates and adds a new region if it doesn't already exist.
51012      * @param {String} target The target region key (north, south, east, west or center).
51013      * @param {Object} config The regions config object
51014      * @return {BorderLayoutRegion} The new region
51015      */
51016     addRegion : function(target, config){
51017         if(!this.regions[target]){
51018             var r = this.factory.create(target, this, config);
51019             this.bindRegion(target, r);
51020         }
51021         return this.regions[target];
51022     },
51023
51024     // private (kinda)
51025     bindRegion : function(name, r){
51026         this.regions[name] = r;
51027         r.on("visibilitychange", this.layout, this);
51028         r.on("paneladded", this.layout, this);
51029         r.on("panelremoved", this.layout, this);
51030         r.on("invalidated", this.layout, this);
51031         r.on("resized", this.onRegionResized, this);
51032         r.on("collapsed", this.onRegionCollapsed, this);
51033         r.on("expanded", this.onRegionExpanded, this);
51034     },
51035
51036     /**
51037      * Performs a layout update.
51038      */
51039     layout : function(){
51040         if(this.updating) {
51041             return;
51042         }
51043         var size = this.getViewSize();
51044         var w = size.width;
51045         var h = size.height;
51046         var centerW = w;
51047         var centerH = h;
51048         var centerY = 0;
51049         var centerX = 0;
51050         //var x = 0, y = 0;
51051
51052         var rs = this.regions;
51053         var north = rs["north"];
51054         var south = rs["south"]; 
51055         var west = rs["west"];
51056         var east = rs["east"];
51057         var center = rs["center"];
51058         //if(this.hideOnLayout){ // not supported anymore
51059             //c.el.setStyle("display", "none");
51060         //}
51061         if(north && north.isVisible()){
51062             var b = north.getBox();
51063             var m = north.getMargins();
51064             b.width = w - (m.left+m.right);
51065             b.x = m.left;
51066             b.y = m.top;
51067             centerY = b.height + b.y + m.bottom;
51068             centerH -= centerY;
51069             north.updateBox(this.safeBox(b));
51070         }
51071         if(south && south.isVisible()){
51072             var b = south.getBox();
51073             var m = south.getMargins();
51074             b.width = w - (m.left+m.right);
51075             b.x = m.left;
51076             var totalHeight = (b.height + m.top + m.bottom);
51077             b.y = h - totalHeight + m.top;
51078             centerH -= totalHeight;
51079             south.updateBox(this.safeBox(b));
51080         }
51081         if(west && west.isVisible()){
51082             var b = west.getBox();
51083             var m = west.getMargins();
51084             b.height = centerH - (m.top+m.bottom);
51085             b.x = m.left;
51086             b.y = centerY + m.top;
51087             var totalWidth = (b.width + m.left + m.right);
51088             centerX += totalWidth;
51089             centerW -= totalWidth;
51090             west.updateBox(this.safeBox(b));
51091         }
51092         if(east && east.isVisible()){
51093             var b = east.getBox();
51094             var m = east.getMargins();
51095             b.height = centerH - (m.top+m.bottom);
51096             var totalWidth = (b.width + m.left + m.right);
51097             b.x = w - totalWidth + m.left;
51098             b.y = centerY + m.top;
51099             centerW -= totalWidth;
51100             east.updateBox(this.safeBox(b));
51101         }
51102         if(center){
51103             var m = center.getMargins();
51104             var centerBox = {
51105                 x: centerX + m.left,
51106                 y: centerY + m.top,
51107                 width: centerW - (m.left+m.right),
51108                 height: centerH - (m.top+m.bottom)
51109             };
51110             //if(this.hideOnLayout){
51111                 //center.el.setStyle("display", "block");
51112             //}
51113             center.updateBox(this.safeBox(centerBox));
51114         }
51115         this.el.repaint();
51116         this.fireEvent("layout", this);
51117     },
51118
51119     // private
51120     safeBox : function(box){
51121         box.width = Math.max(0, box.width);
51122         box.height = Math.max(0, box.height);
51123         return box;
51124     },
51125
51126     /**
51127      * Adds a ContentPanel (or subclass) to this layout.
51128      * @param {String} target The target region key (north, south, east, west or center).
51129      * @param {Roo.ContentPanel} panel The panel to add
51130      * @return {Roo.ContentPanel} The added panel
51131      */
51132     add : function(target, panel){
51133          
51134         target = target.toLowerCase();
51135         return this.regions[target].add(panel);
51136     },
51137
51138     /**
51139      * Remove a ContentPanel (or subclass) to this layout.
51140      * @param {String} target The target region key (north, south, east, west or center).
51141      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51142      * @return {Roo.ContentPanel} The removed panel
51143      */
51144     remove : function(target, panel){
51145         target = target.toLowerCase();
51146         return this.regions[target].remove(panel);
51147     },
51148
51149     /**
51150      * Searches all regions for a panel with the specified id
51151      * @param {String} panelId
51152      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51153      */
51154     findPanel : function(panelId){
51155         var rs = this.regions;
51156         for(var target in rs){
51157             if(typeof rs[target] != "function"){
51158                 var p = rs[target].getPanel(panelId);
51159                 if(p){
51160                     return p;
51161                 }
51162             }
51163         }
51164         return null;
51165     },
51166
51167     /**
51168      * Searches all regions for a panel with the specified id and activates (shows) it.
51169      * @param {String/ContentPanel} panelId The panels id or the panel itself
51170      * @return {Roo.ContentPanel} The shown panel or null
51171      */
51172     showPanel : function(panelId) {
51173       var rs = this.regions;
51174       for(var target in rs){
51175          var r = rs[target];
51176          if(typeof r != "function"){
51177             if(r.hasPanel(panelId)){
51178                return r.showPanel(panelId);
51179             }
51180          }
51181       }
51182       return null;
51183    },
51184
51185    /**
51186      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51187      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51188      */
51189     restoreState : function(provider){
51190         if(!provider){
51191             provider = Roo.state.Manager;
51192         }
51193         var sm = new Roo.LayoutStateManager();
51194         sm.init(this, provider);
51195     },
51196
51197     /**
51198      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51199      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51200      * a valid ContentPanel config object.  Example:
51201      * <pre><code>
51202 // Create the main layout
51203 var layout = new Roo.BorderLayout('main-ct', {
51204     west: {
51205         split:true,
51206         minSize: 175,
51207         titlebar: true
51208     },
51209     center: {
51210         title:'Components'
51211     }
51212 }, 'main-ct');
51213
51214 // Create and add multiple ContentPanels at once via configs
51215 layout.batchAdd({
51216    west: {
51217        id: 'source-files',
51218        autoCreate:true,
51219        title:'Ext Source Files',
51220        autoScroll:true,
51221        fitToFrame:true
51222    },
51223    center : {
51224        el: cview,
51225        autoScroll:true,
51226        fitToFrame:true,
51227        toolbar: tb,
51228        resizeEl:'cbody'
51229    }
51230 });
51231 </code></pre>
51232      * @param {Object} regions An object containing ContentPanel configs by region name
51233      */
51234     batchAdd : function(regions){
51235         this.beginUpdate();
51236         for(var rname in regions){
51237             var lr = this.regions[rname];
51238             if(lr){
51239                 this.addTypedPanels(lr, regions[rname]);
51240             }
51241         }
51242         this.endUpdate();
51243     },
51244
51245     // private
51246     addTypedPanels : function(lr, ps){
51247         if(typeof ps == 'string'){
51248             lr.add(new Roo.ContentPanel(ps));
51249         }
51250         else if(ps instanceof Array){
51251             for(var i =0, len = ps.length; i < len; i++){
51252                 this.addTypedPanels(lr, ps[i]);
51253             }
51254         }
51255         else if(!ps.events){ // raw config?
51256             var el = ps.el;
51257             delete ps.el; // prevent conflict
51258             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51259         }
51260         else {  // panel object assumed!
51261             lr.add(ps);
51262         }
51263     },
51264     /**
51265      * Adds a xtype elements to the layout.
51266      * <pre><code>
51267
51268 layout.addxtype({
51269        xtype : 'ContentPanel',
51270        region: 'west',
51271        items: [ .... ]
51272    }
51273 );
51274
51275 layout.addxtype({
51276         xtype : 'NestedLayoutPanel',
51277         region: 'west',
51278         layout: {
51279            center: { },
51280            west: { }   
51281         },
51282         items : [ ... list of content panels or nested layout panels.. ]
51283    }
51284 );
51285 </code></pre>
51286      * @param {Object} cfg Xtype definition of item to add.
51287      */
51288     addxtype : function(cfg)
51289     {
51290         // basically accepts a pannel...
51291         // can accept a layout region..!?!?
51292         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51293         
51294         if (!cfg.xtype.match(/Panel$/)) {
51295             return false;
51296         }
51297         var ret = false;
51298         
51299         if (typeof(cfg.region) == 'undefined') {
51300             Roo.log("Failed to add Panel, region was not set");
51301             Roo.log(cfg);
51302             return false;
51303         }
51304         var region = cfg.region;
51305         delete cfg.region;
51306         
51307           
51308         var xitems = [];
51309         if (cfg.items) {
51310             xitems = cfg.items;
51311             delete cfg.items;
51312         }
51313         var nb = false;
51314         
51315         switch(cfg.xtype) 
51316         {
51317             case 'ContentPanel':  // ContentPanel (el, cfg)
51318             case 'ScrollPanel':  // ContentPanel (el, cfg)
51319             case 'ViewPanel': 
51320                 if(cfg.autoCreate) {
51321                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51322                 } else {
51323                     var el = this.el.createChild();
51324                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51325                 }
51326                 
51327                 this.add(region, ret);
51328                 break;
51329             
51330             
51331             case 'TreePanel': // our new panel!
51332                 cfg.el = this.el.createChild();
51333                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51334                 this.add(region, ret);
51335                 break;
51336             
51337             case 'NestedLayoutPanel': 
51338                 // create a new Layout (which is  a Border Layout...
51339                 var el = this.el.createChild();
51340                 var clayout = cfg.layout;
51341                 delete cfg.layout;
51342                 clayout.items   = clayout.items  || [];
51343                 // replace this exitems with the clayout ones..
51344                 xitems = clayout.items;
51345                  
51346                 
51347                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51348                     cfg.background = false;
51349                 }
51350                 var layout = new Roo.BorderLayout(el, clayout);
51351                 
51352                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51353                 //console.log('adding nested layout panel '  + cfg.toSource());
51354                 this.add(region, ret);
51355                 nb = {}; /// find first...
51356                 break;
51357                 
51358             case 'GridPanel': 
51359             
51360                 // needs grid and region
51361                 
51362                 //var el = this.getRegion(region).el.createChild();
51363                 var el = this.el.createChild();
51364                 // create the grid first...
51365                 
51366                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51367                 delete cfg.grid;
51368                 if (region == 'center' && this.active ) {
51369                     cfg.background = false;
51370                 }
51371                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51372                 
51373                 this.add(region, ret);
51374                 if (cfg.background) {
51375                     ret.on('activate', function(gp) {
51376                         if (!gp.grid.rendered) {
51377                             gp.grid.render();
51378                         }
51379                     });
51380                 } else {
51381                     grid.render();
51382                 }
51383                 break;
51384            
51385            
51386            
51387                 
51388                 
51389                 
51390             default:
51391                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51392                     
51393                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51394                     this.add(region, ret);
51395                 } else {
51396                 
51397                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51398                     return null;
51399                 }
51400                 
51401              // GridPanel (grid, cfg)
51402             
51403         }
51404         this.beginUpdate();
51405         // add children..
51406         var region = '';
51407         var abn = {};
51408         Roo.each(xitems, function(i)  {
51409             region = nb && i.region ? i.region : false;
51410             
51411             var add = ret.addxtype(i);
51412            
51413             if (region) {
51414                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51415                 if (!i.background) {
51416                     abn[region] = nb[region] ;
51417                 }
51418             }
51419             
51420         });
51421         this.endUpdate();
51422
51423         // make the last non-background panel active..
51424         //if (nb) { Roo.log(abn); }
51425         if (nb) {
51426             
51427             for(var r in abn) {
51428                 region = this.getRegion(r);
51429                 if (region) {
51430                     // tried using nb[r], but it does not work..
51431                      
51432                     region.showPanel(abn[r]);
51433                    
51434                 }
51435             }
51436         }
51437         return ret;
51438         
51439     }
51440 });
51441
51442 /**
51443  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51444  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51445  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51446  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51447  * <pre><code>
51448 // shorthand
51449 var CP = Roo.ContentPanel;
51450
51451 var layout = Roo.BorderLayout.create({
51452     north: {
51453         initialSize: 25,
51454         titlebar: false,
51455         panels: [new CP("north", "North")]
51456     },
51457     west: {
51458         split:true,
51459         initialSize: 200,
51460         minSize: 175,
51461         maxSize: 400,
51462         titlebar: true,
51463         collapsible: true,
51464         panels: [new CP("west", {title: "West"})]
51465     },
51466     east: {
51467         split:true,
51468         initialSize: 202,
51469         minSize: 175,
51470         maxSize: 400,
51471         titlebar: true,
51472         collapsible: true,
51473         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51474     },
51475     south: {
51476         split:true,
51477         initialSize: 100,
51478         minSize: 100,
51479         maxSize: 200,
51480         titlebar: true,
51481         collapsible: true,
51482         panels: [new CP("south", {title: "South", closable: true})]
51483     },
51484     center: {
51485         titlebar: true,
51486         autoScroll:true,
51487         resizeTabs: true,
51488         minTabWidth: 50,
51489         preferredTabWidth: 150,
51490         panels: [
51491             new CP("center1", {title: "Close Me", closable: true}),
51492             new CP("center2", {title: "Center Panel", closable: false})
51493         ]
51494     }
51495 }, document.body);
51496
51497 layout.getRegion("center").showPanel("center1");
51498 </code></pre>
51499  * @param config
51500  * @param targetEl
51501  */
51502 Roo.BorderLayout.create = function(config, targetEl){
51503     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51504     layout.beginUpdate();
51505     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51506     for(var j = 0, jlen = regions.length; j < jlen; j++){
51507         var lr = regions[j];
51508         if(layout.regions[lr] && config[lr].panels){
51509             var r = layout.regions[lr];
51510             var ps = config[lr].panels;
51511             layout.addTypedPanels(r, ps);
51512         }
51513     }
51514     layout.endUpdate();
51515     return layout;
51516 };
51517
51518 // private
51519 Roo.BorderLayout.RegionFactory = {
51520     // private
51521     validRegions : ["north","south","east","west","center"],
51522
51523     // private
51524     create : function(target, mgr, config){
51525         target = target.toLowerCase();
51526         if(config.lightweight || config.basic){
51527             return new Roo.BasicLayoutRegion(mgr, config, target);
51528         }
51529         switch(target){
51530             case "north":
51531                 return new Roo.NorthLayoutRegion(mgr, config);
51532             case "south":
51533                 return new Roo.SouthLayoutRegion(mgr, config);
51534             case "east":
51535                 return new Roo.EastLayoutRegion(mgr, config);
51536             case "west":
51537                 return new Roo.WestLayoutRegion(mgr, config);
51538             case "center":
51539                 return new Roo.CenterLayoutRegion(mgr, config);
51540         }
51541         throw 'Layout region "'+target+'" not supported.';
51542     }
51543 };/*
51544  * Based on:
51545  * Ext JS Library 1.1.1
51546  * Copyright(c) 2006-2007, Ext JS, LLC.
51547  *
51548  * Originally Released Under LGPL - original licence link has changed is not relivant.
51549  *
51550  * Fork - LGPL
51551  * <script type="text/javascript">
51552  */
51553  
51554 /**
51555  * @class Roo.BasicLayoutRegion
51556  * @extends Roo.util.Observable
51557  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51558  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51559  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51560  */
51561 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51562     this.mgr = mgr;
51563     this.position  = pos;
51564     this.events = {
51565         /**
51566          * @scope Roo.BasicLayoutRegion
51567          */
51568         
51569         /**
51570          * @event beforeremove
51571          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51572          * @param {Roo.LayoutRegion} this
51573          * @param {Roo.ContentPanel} panel The panel
51574          * @param {Object} e The cancel event object
51575          */
51576         "beforeremove" : true,
51577         /**
51578          * @event invalidated
51579          * Fires when the layout for this region is changed.
51580          * @param {Roo.LayoutRegion} this
51581          */
51582         "invalidated" : true,
51583         /**
51584          * @event visibilitychange
51585          * Fires when this region is shown or hidden 
51586          * @param {Roo.LayoutRegion} this
51587          * @param {Boolean} visibility true or false
51588          */
51589         "visibilitychange" : true,
51590         /**
51591          * @event paneladded
51592          * Fires when a panel is added. 
51593          * @param {Roo.LayoutRegion} this
51594          * @param {Roo.ContentPanel} panel The panel
51595          */
51596         "paneladded" : true,
51597         /**
51598          * @event panelremoved
51599          * Fires when a panel is removed. 
51600          * @param {Roo.LayoutRegion} this
51601          * @param {Roo.ContentPanel} panel The panel
51602          */
51603         "panelremoved" : true,
51604         /**
51605          * @event beforecollapse
51606          * Fires when this region before collapse.
51607          * @param {Roo.LayoutRegion} this
51608          */
51609         "beforecollapse" : true,
51610         /**
51611          * @event collapsed
51612          * Fires when this region is collapsed.
51613          * @param {Roo.LayoutRegion} this
51614          */
51615         "collapsed" : true,
51616         /**
51617          * @event expanded
51618          * Fires when this region is expanded.
51619          * @param {Roo.LayoutRegion} this
51620          */
51621         "expanded" : true,
51622         /**
51623          * @event slideshow
51624          * Fires when this region is slid into view.
51625          * @param {Roo.LayoutRegion} this
51626          */
51627         "slideshow" : true,
51628         /**
51629          * @event slidehide
51630          * Fires when this region slides out of view. 
51631          * @param {Roo.LayoutRegion} this
51632          */
51633         "slidehide" : true,
51634         /**
51635          * @event panelactivated
51636          * Fires when a panel is activated. 
51637          * @param {Roo.LayoutRegion} this
51638          * @param {Roo.ContentPanel} panel The activated panel
51639          */
51640         "panelactivated" : true,
51641         /**
51642          * @event resized
51643          * Fires when the user resizes this region. 
51644          * @param {Roo.LayoutRegion} this
51645          * @param {Number} newSize The new size (width for east/west, height for north/south)
51646          */
51647         "resized" : true
51648     };
51649     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51650     this.panels = new Roo.util.MixedCollection();
51651     this.panels.getKey = this.getPanelId.createDelegate(this);
51652     this.box = null;
51653     this.activePanel = null;
51654     // ensure listeners are added...
51655     
51656     if (config.listeners || config.events) {
51657         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51658             listeners : config.listeners || {},
51659             events : config.events || {}
51660         });
51661     }
51662     
51663     if(skipConfig !== true){
51664         this.applyConfig(config);
51665     }
51666 };
51667
51668 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51669     getPanelId : function(p){
51670         return p.getId();
51671     },
51672     
51673     applyConfig : function(config){
51674         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51675         this.config = config;
51676         
51677     },
51678     
51679     /**
51680      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51681      * the width, for horizontal (north, south) the height.
51682      * @param {Number} newSize The new width or height
51683      */
51684     resizeTo : function(newSize){
51685         var el = this.el ? this.el :
51686                  (this.activePanel ? this.activePanel.getEl() : null);
51687         if(el){
51688             switch(this.position){
51689                 case "east":
51690                 case "west":
51691                     el.setWidth(newSize);
51692                     this.fireEvent("resized", this, newSize);
51693                 break;
51694                 case "north":
51695                 case "south":
51696                     el.setHeight(newSize);
51697                     this.fireEvent("resized", this, newSize);
51698                 break;                
51699             }
51700         }
51701     },
51702     
51703     getBox : function(){
51704         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51705     },
51706     
51707     getMargins : function(){
51708         return this.margins;
51709     },
51710     
51711     updateBox : function(box){
51712         this.box = box;
51713         var el = this.activePanel.getEl();
51714         el.dom.style.left = box.x + "px";
51715         el.dom.style.top = box.y + "px";
51716         this.activePanel.setSize(box.width, box.height);
51717     },
51718     
51719     /**
51720      * Returns the container element for this region.
51721      * @return {Roo.Element}
51722      */
51723     getEl : function(){
51724         return this.activePanel;
51725     },
51726     
51727     /**
51728      * Returns true if this region is currently visible.
51729      * @return {Boolean}
51730      */
51731     isVisible : function(){
51732         return this.activePanel ? true : false;
51733     },
51734     
51735     setActivePanel : function(panel){
51736         panel = this.getPanel(panel);
51737         if(this.activePanel && this.activePanel != panel){
51738             this.activePanel.setActiveState(false);
51739             this.activePanel.getEl().setLeftTop(-10000,-10000);
51740         }
51741         this.activePanel = panel;
51742         panel.setActiveState(true);
51743         if(this.box){
51744             panel.setSize(this.box.width, this.box.height);
51745         }
51746         this.fireEvent("panelactivated", this, panel);
51747         this.fireEvent("invalidated");
51748     },
51749     
51750     /**
51751      * Show the specified panel.
51752      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51753      * @return {Roo.ContentPanel} The shown panel or null
51754      */
51755     showPanel : function(panel){
51756         if(panel = this.getPanel(panel)){
51757             this.setActivePanel(panel);
51758         }
51759         return panel;
51760     },
51761     
51762     /**
51763      * Get the active panel for this region.
51764      * @return {Roo.ContentPanel} The active panel or null
51765      */
51766     getActivePanel : function(){
51767         return this.activePanel;
51768     },
51769     
51770     /**
51771      * Add the passed ContentPanel(s)
51772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51773      * @return {Roo.ContentPanel} The panel added (if only one was added)
51774      */
51775     add : function(panel){
51776         if(arguments.length > 1){
51777             for(var i = 0, len = arguments.length; i < len; i++) {
51778                 this.add(arguments[i]);
51779             }
51780             return null;
51781         }
51782         if(this.hasPanel(panel)){
51783             this.showPanel(panel);
51784             return panel;
51785         }
51786         var el = panel.getEl();
51787         if(el.dom.parentNode != this.mgr.el.dom){
51788             this.mgr.el.dom.appendChild(el.dom);
51789         }
51790         if(panel.setRegion){
51791             panel.setRegion(this);
51792         }
51793         this.panels.add(panel);
51794         el.setStyle("position", "absolute");
51795         if(!panel.background){
51796             this.setActivePanel(panel);
51797             if(this.config.initialSize && this.panels.getCount()==1){
51798                 this.resizeTo(this.config.initialSize);
51799             }
51800         }
51801         this.fireEvent("paneladded", this, panel);
51802         return panel;
51803     },
51804     
51805     /**
51806      * Returns true if the panel is in this region.
51807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51808      * @return {Boolean}
51809      */
51810     hasPanel : function(panel){
51811         if(typeof panel == "object"){ // must be panel obj
51812             panel = panel.getId();
51813         }
51814         return this.getPanel(panel) ? true : false;
51815     },
51816     
51817     /**
51818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51820      * @param {Boolean} preservePanel Overrides the config preservePanel option
51821      * @return {Roo.ContentPanel} The panel that was removed
51822      */
51823     remove : function(panel, preservePanel){
51824         panel = this.getPanel(panel);
51825         if(!panel){
51826             return null;
51827         }
51828         var e = {};
51829         this.fireEvent("beforeremove", this, panel, e);
51830         if(e.cancel === true){
51831             return null;
51832         }
51833         var panelId = panel.getId();
51834         this.panels.removeKey(panelId);
51835         return panel;
51836     },
51837     
51838     /**
51839      * Returns the panel specified or null if it's not in this region.
51840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51841      * @return {Roo.ContentPanel}
51842      */
51843     getPanel : function(id){
51844         if(typeof id == "object"){ // must be panel obj
51845             return id;
51846         }
51847         return this.panels.get(id);
51848     },
51849     
51850     /**
51851      * Returns this regions position (north/south/east/west/center).
51852      * @return {String} 
51853      */
51854     getPosition: function(){
51855         return this.position;    
51856     }
51857 });/*
51858  * Based on:
51859  * Ext JS Library 1.1.1
51860  * Copyright(c) 2006-2007, Ext JS, LLC.
51861  *
51862  * Originally Released Under LGPL - original licence link has changed is not relivant.
51863  *
51864  * Fork - LGPL
51865  * <script type="text/javascript">
51866  */
51867  
51868 /**
51869  * @class Roo.LayoutRegion
51870  * @extends Roo.BasicLayoutRegion
51871  * This class represents a region in a layout manager.
51872  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51873  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51874  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51875  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51876  * @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})
51877  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51878  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51879  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51880  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51881  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51882  * @cfg {String}    title           The title for the region (overrides panel titles)
51883  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51884  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51885  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51886  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51887  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51888  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51889  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51890  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51891  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51892  * @cfg {Boolean}   showPin         True to show a pin button
51893  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51894  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51895  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51896  * @cfg {Number}    width           For East/West panels
51897  * @cfg {Number}    height          For North/South panels
51898  * @cfg {Boolean}   split           To show the splitter
51899  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51900  */
51901 Roo.LayoutRegion = function(mgr, config, pos){
51902     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51903     var dh = Roo.DomHelper;
51904     /** This region's container element 
51905     * @type Roo.Element */
51906     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51907     /** This region's title element 
51908     * @type Roo.Element */
51909
51910     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51911         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51912         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51913     ]}, true);
51914     this.titleEl.enableDisplayMode();
51915     /** This region's title text element 
51916     * @type HTMLElement */
51917     this.titleTextEl = this.titleEl.dom.firstChild;
51918     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51919     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51920     this.closeBtn.enableDisplayMode();
51921     this.closeBtn.on("click", this.closeClicked, this);
51922     this.closeBtn.hide();
51923
51924     this.createBody(config);
51925     this.visible = true;
51926     this.collapsed = false;
51927
51928     if(config.hideWhenEmpty){
51929         this.hide();
51930         this.on("paneladded", this.validateVisibility, this);
51931         this.on("panelremoved", this.validateVisibility, this);
51932     }
51933     this.applyConfig(config);
51934 };
51935
51936 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51937
51938     createBody : function(){
51939         /** This region's body element 
51940         * @type Roo.Element */
51941         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51942     },
51943
51944     applyConfig : function(c){
51945         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51946             var dh = Roo.DomHelper;
51947             if(c.titlebar !== false){
51948                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51949                 this.collapseBtn.on("click", this.collapse, this);
51950                 this.collapseBtn.enableDisplayMode();
51951
51952                 if(c.showPin === true || this.showPin){
51953                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51954                     this.stickBtn.enableDisplayMode();
51955                     this.stickBtn.on("click", this.expand, this);
51956                     this.stickBtn.hide();
51957                 }
51958             }
51959             /** This region's collapsed element
51960             * @type Roo.Element */
51961             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51962                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51963             ]}, true);
51964             if(c.floatable !== false){
51965                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51966                this.collapsedEl.on("click", this.collapseClick, this);
51967             }
51968
51969             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51970                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51971                    id: "message", unselectable: "on", style:{"float":"left"}});
51972                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51973              }
51974             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51975             this.expandBtn.on("click", this.expand, this);
51976         }
51977         if(this.collapseBtn){
51978             this.collapseBtn.setVisible(c.collapsible == true);
51979         }
51980         this.cmargins = c.cmargins || this.cmargins ||
51981                          (this.position == "west" || this.position == "east" ?
51982                              {top: 0, left: 2, right:2, bottom: 0} :
51983                              {top: 2, left: 0, right:0, bottom: 2});
51984         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51985         this.bottomTabs = c.tabPosition != "top";
51986         this.autoScroll = c.autoScroll || false;
51987         if(this.autoScroll){
51988             this.bodyEl.setStyle("overflow", "auto");
51989         }else{
51990             this.bodyEl.setStyle("overflow", "hidden");
51991         }
51992         //if(c.titlebar !== false){
51993             if((!c.titlebar && !c.title) || c.titlebar === false){
51994                 this.titleEl.hide();
51995             }else{
51996                 this.titleEl.show();
51997                 if(c.title){
51998                     this.titleTextEl.innerHTML = c.title;
51999                 }
52000             }
52001         //}
52002         this.duration = c.duration || .30;
52003         this.slideDuration = c.slideDuration || .45;
52004         this.config = c;
52005         if(c.collapsed){
52006             this.collapse(true);
52007         }
52008         if(c.hidden){
52009             this.hide();
52010         }
52011     },
52012     /**
52013      * Returns true if this region is currently visible.
52014      * @return {Boolean}
52015      */
52016     isVisible : function(){
52017         return this.visible;
52018     },
52019
52020     /**
52021      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52022      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52023      */
52024     setCollapsedTitle : function(title){
52025         title = title || "&#160;";
52026         if(this.collapsedTitleTextEl){
52027             this.collapsedTitleTextEl.innerHTML = title;
52028         }
52029     },
52030
52031     getBox : function(){
52032         var b;
52033         if(!this.collapsed){
52034             b = this.el.getBox(false, true);
52035         }else{
52036             b = this.collapsedEl.getBox(false, true);
52037         }
52038         return b;
52039     },
52040
52041     getMargins : function(){
52042         return this.collapsed ? this.cmargins : this.margins;
52043     },
52044
52045     highlight : function(){
52046         this.el.addClass("x-layout-panel-dragover");
52047     },
52048
52049     unhighlight : function(){
52050         this.el.removeClass("x-layout-panel-dragover");
52051     },
52052
52053     updateBox : function(box){
52054         this.box = box;
52055         if(!this.collapsed){
52056             this.el.dom.style.left = box.x + "px";
52057             this.el.dom.style.top = box.y + "px";
52058             this.updateBody(box.width, box.height);
52059         }else{
52060             this.collapsedEl.dom.style.left = box.x + "px";
52061             this.collapsedEl.dom.style.top = box.y + "px";
52062             this.collapsedEl.setSize(box.width, box.height);
52063         }
52064         if(this.tabs){
52065             this.tabs.autoSizeTabs();
52066         }
52067     },
52068
52069     updateBody : function(w, h){
52070         if(w !== null){
52071             this.el.setWidth(w);
52072             w -= this.el.getBorderWidth("rl");
52073             if(this.config.adjustments){
52074                 w += this.config.adjustments[0];
52075             }
52076         }
52077         if(h !== null){
52078             this.el.setHeight(h);
52079             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52080             h -= this.el.getBorderWidth("tb");
52081             if(this.config.adjustments){
52082                 h += this.config.adjustments[1];
52083             }
52084             this.bodyEl.setHeight(h);
52085             if(this.tabs){
52086                 h = this.tabs.syncHeight(h);
52087             }
52088         }
52089         if(this.panelSize){
52090             w = w !== null ? w : this.panelSize.width;
52091             h = h !== null ? h : this.panelSize.height;
52092         }
52093         if(this.activePanel){
52094             var el = this.activePanel.getEl();
52095             w = w !== null ? w : el.getWidth();
52096             h = h !== null ? h : el.getHeight();
52097             this.panelSize = {width: w, height: h};
52098             this.activePanel.setSize(w, h);
52099         }
52100         if(Roo.isIE && this.tabs){
52101             this.tabs.el.repaint();
52102         }
52103     },
52104
52105     /**
52106      * Returns the container element for this region.
52107      * @return {Roo.Element}
52108      */
52109     getEl : function(){
52110         return this.el;
52111     },
52112
52113     /**
52114      * Hides this region.
52115      */
52116     hide : function(){
52117         if(!this.collapsed){
52118             this.el.dom.style.left = "-2000px";
52119             this.el.hide();
52120         }else{
52121             this.collapsedEl.dom.style.left = "-2000px";
52122             this.collapsedEl.hide();
52123         }
52124         this.visible = false;
52125         this.fireEvent("visibilitychange", this, false);
52126     },
52127
52128     /**
52129      * Shows this region if it was previously hidden.
52130      */
52131     show : function(){
52132         if(!this.collapsed){
52133             this.el.show();
52134         }else{
52135             this.collapsedEl.show();
52136         }
52137         this.visible = true;
52138         this.fireEvent("visibilitychange", this, true);
52139     },
52140
52141     closeClicked : function(){
52142         if(this.activePanel){
52143             this.remove(this.activePanel);
52144         }
52145     },
52146
52147     collapseClick : function(e){
52148         if(this.isSlid){
52149            e.stopPropagation();
52150            this.slideIn();
52151         }else{
52152            e.stopPropagation();
52153            this.slideOut();
52154         }
52155     },
52156
52157     /**
52158      * Collapses this region.
52159      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52160      */
52161     collapse : function(skipAnim, skipCheck = false){
52162         if(this.collapsed) {
52163             return;
52164         }
52165         
52166         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52167             
52168             this.collapsed = true;
52169             if(this.split){
52170                 this.split.el.hide();
52171             }
52172             if(this.config.animate && skipAnim !== true){
52173                 this.fireEvent("invalidated", this);
52174                 this.animateCollapse();
52175             }else{
52176                 this.el.setLocation(-20000,-20000);
52177                 this.el.hide();
52178                 this.collapsedEl.show();
52179                 this.fireEvent("collapsed", this);
52180                 this.fireEvent("invalidated", this);
52181             }
52182         }
52183         
52184     },
52185
52186     animateCollapse : function(){
52187         // overridden
52188     },
52189
52190     /**
52191      * Expands this region if it was previously collapsed.
52192      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52193      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52194      */
52195     expand : function(e, skipAnim){
52196         if(e) {
52197             e.stopPropagation();
52198         }
52199         if(!this.collapsed || this.el.hasActiveFx()) {
52200             return;
52201         }
52202         if(this.isSlid){
52203             this.afterSlideIn();
52204             skipAnim = true;
52205         }
52206         this.collapsed = false;
52207         if(this.config.animate && skipAnim !== true){
52208             this.animateExpand();
52209         }else{
52210             this.el.show();
52211             if(this.split){
52212                 this.split.el.show();
52213             }
52214             this.collapsedEl.setLocation(-2000,-2000);
52215             this.collapsedEl.hide();
52216             this.fireEvent("invalidated", this);
52217             this.fireEvent("expanded", this);
52218         }
52219     },
52220
52221     animateExpand : function(){
52222         // overridden
52223     },
52224
52225     initTabs : function()
52226     {
52227         this.bodyEl.setStyle("overflow", "hidden");
52228         var ts = new Roo.TabPanel(
52229                 this.bodyEl.dom,
52230                 {
52231                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52232                     disableTooltips: this.config.disableTabTips,
52233                     toolbar : this.config.toolbar
52234                 }
52235         );
52236         if(this.config.hideTabs){
52237             ts.stripWrap.setDisplayed(false);
52238         }
52239         this.tabs = ts;
52240         ts.resizeTabs = this.config.resizeTabs === true;
52241         ts.minTabWidth = this.config.minTabWidth || 40;
52242         ts.maxTabWidth = this.config.maxTabWidth || 250;
52243         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52244         ts.monitorResize = false;
52245         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52246         ts.bodyEl.addClass('x-layout-tabs-body');
52247         this.panels.each(this.initPanelAsTab, this);
52248     },
52249
52250     initPanelAsTab : function(panel){
52251         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52252                     this.config.closeOnTab && panel.isClosable());
52253         if(panel.tabTip !== undefined){
52254             ti.setTooltip(panel.tabTip);
52255         }
52256         ti.on("activate", function(){
52257               this.setActivePanel(panel);
52258         }, this);
52259         if(this.config.closeOnTab){
52260             ti.on("beforeclose", function(t, e){
52261                 e.cancel = true;
52262                 this.remove(panel);
52263             }, this);
52264         }
52265         return ti;
52266     },
52267
52268     updatePanelTitle : function(panel, title){
52269         if(this.activePanel == panel){
52270             this.updateTitle(title);
52271         }
52272         if(this.tabs){
52273             var ti = this.tabs.getTab(panel.getEl().id);
52274             ti.setText(title);
52275             if(panel.tabTip !== undefined){
52276                 ti.setTooltip(panel.tabTip);
52277             }
52278         }
52279     },
52280
52281     updateTitle : function(title){
52282         if(this.titleTextEl && !this.config.title){
52283             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52284         }
52285     },
52286
52287     setActivePanel : function(panel){
52288         panel = this.getPanel(panel);
52289         if(this.activePanel && this.activePanel != panel){
52290             this.activePanel.setActiveState(false);
52291         }
52292         this.activePanel = panel;
52293         panel.setActiveState(true);
52294         if(this.panelSize){
52295             panel.setSize(this.panelSize.width, this.panelSize.height);
52296         }
52297         if(this.closeBtn){
52298             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52299         }
52300         this.updateTitle(panel.getTitle());
52301         if(this.tabs){
52302             this.fireEvent("invalidated", this);
52303         }
52304         this.fireEvent("panelactivated", this, panel);
52305     },
52306
52307     /**
52308      * Shows the specified panel.
52309      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52310      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52311      */
52312     showPanel : function(panel)
52313     {
52314         panel = this.getPanel(panel);
52315         if(panel){
52316             if(this.tabs){
52317                 var tab = this.tabs.getTab(panel.getEl().id);
52318                 if(tab.isHidden()){
52319                     this.tabs.unhideTab(tab.id);
52320                 }
52321                 tab.activate();
52322             }else{
52323                 this.setActivePanel(panel);
52324             }
52325         }
52326         return panel;
52327     },
52328
52329     /**
52330      * Get the active panel for this region.
52331      * @return {Roo.ContentPanel} The active panel or null
52332      */
52333     getActivePanel : function(){
52334         return this.activePanel;
52335     },
52336
52337     validateVisibility : function(){
52338         if(this.panels.getCount() < 1){
52339             this.updateTitle("&#160;");
52340             this.closeBtn.hide();
52341             this.hide();
52342         }else{
52343             if(!this.isVisible()){
52344                 this.show();
52345             }
52346         }
52347     },
52348
52349     /**
52350      * Adds the passed ContentPanel(s) to this region.
52351      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52352      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52353      */
52354     add : function(panel){
52355         if(arguments.length > 1){
52356             for(var i = 0, len = arguments.length; i < len; i++) {
52357                 this.add(arguments[i]);
52358             }
52359             return null;
52360         }
52361         if(this.hasPanel(panel)){
52362             this.showPanel(panel);
52363             return panel;
52364         }
52365         panel.setRegion(this);
52366         this.panels.add(panel);
52367         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52368             this.bodyEl.dom.appendChild(panel.getEl().dom);
52369             if(panel.background !== true){
52370                 this.setActivePanel(panel);
52371             }
52372             this.fireEvent("paneladded", this, panel);
52373             return panel;
52374         }
52375         if(!this.tabs){
52376             this.initTabs();
52377         }else{
52378             this.initPanelAsTab(panel);
52379         }
52380         if(panel.background !== true){
52381             this.tabs.activate(panel.getEl().id);
52382         }
52383         this.fireEvent("paneladded", this, panel);
52384         return panel;
52385     },
52386
52387     /**
52388      * Hides the tab for the specified panel.
52389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52390      */
52391     hidePanel : function(panel){
52392         if(this.tabs && (panel = this.getPanel(panel))){
52393             this.tabs.hideTab(panel.getEl().id);
52394         }
52395     },
52396
52397     /**
52398      * Unhides the tab for a previously hidden panel.
52399      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52400      */
52401     unhidePanel : function(panel){
52402         if(this.tabs && (panel = this.getPanel(panel))){
52403             this.tabs.unhideTab(panel.getEl().id);
52404         }
52405     },
52406
52407     clearPanels : function(){
52408         while(this.panels.getCount() > 0){
52409              this.remove(this.panels.first());
52410         }
52411     },
52412
52413     /**
52414      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52415      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52416      * @param {Boolean} preservePanel Overrides the config preservePanel option
52417      * @return {Roo.ContentPanel} The panel that was removed
52418      */
52419     remove : function(panel, preservePanel){
52420         panel = this.getPanel(panel);
52421         if(!panel){
52422             return null;
52423         }
52424         var e = {};
52425         this.fireEvent("beforeremove", this, panel, e);
52426         if(e.cancel === true){
52427             return null;
52428         }
52429         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52430         var panelId = panel.getId();
52431         this.panels.removeKey(panelId);
52432         if(preservePanel){
52433             document.body.appendChild(panel.getEl().dom);
52434         }
52435         if(this.tabs){
52436             this.tabs.removeTab(panel.getEl().id);
52437         }else if (!preservePanel){
52438             this.bodyEl.dom.removeChild(panel.getEl().dom);
52439         }
52440         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52441             var p = this.panels.first();
52442             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52443             tempEl.appendChild(p.getEl().dom);
52444             this.bodyEl.update("");
52445             this.bodyEl.dom.appendChild(p.getEl().dom);
52446             tempEl = null;
52447             this.updateTitle(p.getTitle());
52448             this.tabs = null;
52449             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52450             this.setActivePanel(p);
52451         }
52452         panel.setRegion(null);
52453         if(this.activePanel == panel){
52454             this.activePanel = null;
52455         }
52456         if(this.config.autoDestroy !== false && preservePanel !== true){
52457             try{panel.destroy();}catch(e){}
52458         }
52459         this.fireEvent("panelremoved", this, panel);
52460         return panel;
52461     },
52462
52463     /**
52464      * Returns the TabPanel component used by this region
52465      * @return {Roo.TabPanel}
52466      */
52467     getTabs : function(){
52468         return this.tabs;
52469     },
52470
52471     createTool : function(parentEl, className){
52472         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52473             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52474         btn.addClassOnOver("x-layout-tools-button-over");
52475         return btn;
52476     }
52477 });/*
52478  * Based on:
52479  * Ext JS Library 1.1.1
52480  * Copyright(c) 2006-2007, Ext JS, LLC.
52481  *
52482  * Originally Released Under LGPL - original licence link has changed is not relivant.
52483  *
52484  * Fork - LGPL
52485  * <script type="text/javascript">
52486  */
52487  
52488
52489
52490 /**
52491  * @class Roo.SplitLayoutRegion
52492  * @extends Roo.LayoutRegion
52493  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52494  */
52495 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52496     this.cursor = cursor;
52497     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52498 };
52499
52500 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52501     splitTip : "Drag to resize.",
52502     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52503     useSplitTips : false,
52504
52505     applyConfig : function(config){
52506         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52507         if(config.split){
52508             if(!this.split){
52509                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52510                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52511                 /** The SplitBar for this region 
52512                 * @type Roo.SplitBar */
52513                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52514                 this.split.on("moved", this.onSplitMove, this);
52515                 this.split.useShim = config.useShim === true;
52516                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52517                 if(this.useSplitTips){
52518                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52519                 }
52520                 if(config.collapsible){
52521                     this.split.el.on("dblclick", this.collapse,  this);
52522                 }
52523             }
52524             if(typeof config.minSize != "undefined"){
52525                 this.split.minSize = config.minSize;
52526             }
52527             if(typeof config.maxSize != "undefined"){
52528                 this.split.maxSize = config.maxSize;
52529             }
52530             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52531                 this.hideSplitter();
52532             }
52533         }
52534     },
52535
52536     getHMaxSize : function(){
52537          var cmax = this.config.maxSize || 10000;
52538          var center = this.mgr.getRegion("center");
52539          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52540     },
52541
52542     getVMaxSize : function(){
52543          var cmax = this.config.maxSize || 10000;
52544          var center = this.mgr.getRegion("center");
52545          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52546     },
52547
52548     onSplitMove : function(split, newSize){
52549         this.fireEvent("resized", this, newSize);
52550     },
52551     
52552     /** 
52553      * Returns the {@link Roo.SplitBar} for this region.
52554      * @return {Roo.SplitBar}
52555      */
52556     getSplitBar : function(){
52557         return this.split;
52558     },
52559     
52560     hide : function(){
52561         this.hideSplitter();
52562         Roo.SplitLayoutRegion.superclass.hide.call(this);
52563     },
52564
52565     hideSplitter : function(){
52566         if(this.split){
52567             this.split.el.setLocation(-2000,-2000);
52568             this.split.el.hide();
52569         }
52570     },
52571
52572     show : function(){
52573         if(this.split){
52574             this.split.el.show();
52575         }
52576         Roo.SplitLayoutRegion.superclass.show.call(this);
52577     },
52578     
52579     beforeSlide: function(){
52580         if(Roo.isGecko){// firefox overflow auto bug workaround
52581             this.bodyEl.clip();
52582             if(this.tabs) {
52583                 this.tabs.bodyEl.clip();
52584             }
52585             if(this.activePanel){
52586                 this.activePanel.getEl().clip();
52587                 
52588                 if(this.activePanel.beforeSlide){
52589                     this.activePanel.beforeSlide();
52590                 }
52591             }
52592         }
52593     },
52594     
52595     afterSlide : function(){
52596         if(Roo.isGecko){// firefox overflow auto bug workaround
52597             this.bodyEl.unclip();
52598             if(this.tabs) {
52599                 this.tabs.bodyEl.unclip();
52600             }
52601             if(this.activePanel){
52602                 this.activePanel.getEl().unclip();
52603                 if(this.activePanel.afterSlide){
52604                     this.activePanel.afterSlide();
52605                 }
52606             }
52607         }
52608     },
52609
52610     initAutoHide : function(){
52611         if(this.autoHide !== false){
52612             if(!this.autoHideHd){
52613                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52614                 this.autoHideHd = {
52615                     "mouseout": function(e){
52616                         if(!e.within(this.el, true)){
52617                             st.delay(500);
52618                         }
52619                     },
52620                     "mouseover" : function(e){
52621                         st.cancel();
52622                     },
52623                     scope : this
52624                 };
52625             }
52626             this.el.on(this.autoHideHd);
52627         }
52628     },
52629
52630     clearAutoHide : function(){
52631         if(this.autoHide !== false){
52632             this.el.un("mouseout", this.autoHideHd.mouseout);
52633             this.el.un("mouseover", this.autoHideHd.mouseover);
52634         }
52635     },
52636
52637     clearMonitor : function(){
52638         Roo.get(document).un("click", this.slideInIf, this);
52639     },
52640
52641     // these names are backwards but not changed for compat
52642     slideOut : function(){
52643         if(this.isSlid || this.el.hasActiveFx()){
52644             return;
52645         }
52646         this.isSlid = true;
52647         if(this.collapseBtn){
52648             this.collapseBtn.hide();
52649         }
52650         this.closeBtnState = this.closeBtn.getStyle('display');
52651         this.closeBtn.hide();
52652         if(this.stickBtn){
52653             this.stickBtn.show();
52654         }
52655         this.el.show();
52656         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52657         this.beforeSlide();
52658         this.el.setStyle("z-index", 10001);
52659         this.el.slideIn(this.getSlideAnchor(), {
52660             callback: function(){
52661                 this.afterSlide();
52662                 this.initAutoHide();
52663                 Roo.get(document).on("click", this.slideInIf, this);
52664                 this.fireEvent("slideshow", this);
52665             },
52666             scope: this,
52667             block: true
52668         });
52669     },
52670
52671     afterSlideIn : function(){
52672         this.clearAutoHide();
52673         this.isSlid = false;
52674         this.clearMonitor();
52675         this.el.setStyle("z-index", "");
52676         if(this.collapseBtn){
52677             this.collapseBtn.show();
52678         }
52679         this.closeBtn.setStyle('display', this.closeBtnState);
52680         if(this.stickBtn){
52681             this.stickBtn.hide();
52682         }
52683         this.fireEvent("slidehide", this);
52684     },
52685
52686     slideIn : function(cb){
52687         if(!this.isSlid || this.el.hasActiveFx()){
52688             Roo.callback(cb);
52689             return;
52690         }
52691         this.isSlid = false;
52692         this.beforeSlide();
52693         this.el.slideOut(this.getSlideAnchor(), {
52694             callback: function(){
52695                 this.el.setLeftTop(-10000, -10000);
52696                 this.afterSlide();
52697                 this.afterSlideIn();
52698                 Roo.callback(cb);
52699             },
52700             scope: this,
52701             block: true
52702         });
52703     },
52704     
52705     slideInIf : function(e){
52706         if(!e.within(this.el)){
52707             this.slideIn();
52708         }
52709     },
52710
52711     animateCollapse : function(){
52712         this.beforeSlide();
52713         this.el.setStyle("z-index", 20000);
52714         var anchor = this.getSlideAnchor();
52715         this.el.slideOut(anchor, {
52716             callback : function(){
52717                 this.el.setStyle("z-index", "");
52718                 this.collapsedEl.slideIn(anchor, {duration:.3});
52719                 this.afterSlide();
52720                 this.el.setLocation(-10000,-10000);
52721                 this.el.hide();
52722                 this.fireEvent("collapsed", this);
52723             },
52724             scope: this,
52725             block: true
52726         });
52727     },
52728
52729     animateExpand : function(){
52730         this.beforeSlide();
52731         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52732         this.el.setStyle("z-index", 20000);
52733         this.collapsedEl.hide({
52734             duration:.1
52735         });
52736         this.el.slideIn(this.getSlideAnchor(), {
52737             callback : function(){
52738                 this.el.setStyle("z-index", "");
52739                 this.afterSlide();
52740                 if(this.split){
52741                     this.split.el.show();
52742                 }
52743                 this.fireEvent("invalidated", this);
52744                 this.fireEvent("expanded", this);
52745             },
52746             scope: this,
52747             block: true
52748         });
52749     },
52750
52751     anchors : {
52752         "west" : "left",
52753         "east" : "right",
52754         "north" : "top",
52755         "south" : "bottom"
52756     },
52757
52758     sanchors : {
52759         "west" : "l",
52760         "east" : "r",
52761         "north" : "t",
52762         "south" : "b"
52763     },
52764
52765     canchors : {
52766         "west" : "tl-tr",
52767         "east" : "tr-tl",
52768         "north" : "tl-bl",
52769         "south" : "bl-tl"
52770     },
52771
52772     getAnchor : function(){
52773         return this.anchors[this.position];
52774     },
52775
52776     getCollapseAnchor : function(){
52777         return this.canchors[this.position];
52778     },
52779
52780     getSlideAnchor : function(){
52781         return this.sanchors[this.position];
52782     },
52783
52784     getAlignAdj : function(){
52785         var cm = this.cmargins;
52786         switch(this.position){
52787             case "west":
52788                 return [0, 0];
52789             break;
52790             case "east":
52791                 return [0, 0];
52792             break;
52793             case "north":
52794                 return [0, 0];
52795             break;
52796             case "south":
52797                 return [0, 0];
52798             break;
52799         }
52800     },
52801
52802     getExpandAdj : function(){
52803         var c = this.collapsedEl, cm = this.cmargins;
52804         switch(this.position){
52805             case "west":
52806                 return [-(cm.right+c.getWidth()+cm.left), 0];
52807             break;
52808             case "east":
52809                 return [cm.right+c.getWidth()+cm.left, 0];
52810             break;
52811             case "north":
52812                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52813             break;
52814             case "south":
52815                 return [0, cm.top+cm.bottom+c.getHeight()];
52816             break;
52817         }
52818     }
52819 });/*
52820  * Based on:
52821  * Ext JS Library 1.1.1
52822  * Copyright(c) 2006-2007, Ext JS, LLC.
52823  *
52824  * Originally Released Under LGPL - original licence link has changed is not relivant.
52825  *
52826  * Fork - LGPL
52827  * <script type="text/javascript">
52828  */
52829 /*
52830  * These classes are private internal classes
52831  */
52832 Roo.CenterLayoutRegion = function(mgr, config){
52833     Roo.LayoutRegion.call(this, mgr, config, "center");
52834     this.visible = true;
52835     this.minWidth = config.minWidth || 20;
52836     this.minHeight = config.minHeight || 20;
52837 };
52838
52839 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52840     hide : function(){
52841         // center panel can't be hidden
52842     },
52843     
52844     show : function(){
52845         // center panel can't be hidden
52846     },
52847     
52848     getMinWidth: function(){
52849         return this.minWidth;
52850     },
52851     
52852     getMinHeight: function(){
52853         return this.minHeight;
52854     }
52855 });
52856
52857
52858 Roo.NorthLayoutRegion = function(mgr, config){
52859     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52860     if(this.split){
52861         this.split.placement = Roo.SplitBar.TOP;
52862         this.split.orientation = Roo.SplitBar.VERTICAL;
52863         this.split.el.addClass("x-layout-split-v");
52864     }
52865     var size = config.initialSize || config.height;
52866     if(typeof size != "undefined"){
52867         this.el.setHeight(size);
52868     }
52869 };
52870 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52871     orientation: Roo.SplitBar.VERTICAL,
52872     getBox : function(){
52873         if(this.collapsed){
52874             return this.collapsedEl.getBox();
52875         }
52876         var box = this.el.getBox();
52877         if(this.split){
52878             box.height += this.split.el.getHeight();
52879         }
52880         return box;
52881     },
52882     
52883     updateBox : function(box){
52884         if(this.split && !this.collapsed){
52885             box.height -= this.split.el.getHeight();
52886             this.split.el.setLeft(box.x);
52887             this.split.el.setTop(box.y+box.height);
52888             this.split.el.setWidth(box.width);
52889         }
52890         if(this.collapsed){
52891             this.updateBody(box.width, null);
52892         }
52893         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52894     }
52895 });
52896
52897 Roo.SouthLayoutRegion = function(mgr, config){
52898     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52899     if(this.split){
52900         this.split.placement = Roo.SplitBar.BOTTOM;
52901         this.split.orientation = Roo.SplitBar.VERTICAL;
52902         this.split.el.addClass("x-layout-split-v");
52903     }
52904     var size = config.initialSize || config.height;
52905     if(typeof size != "undefined"){
52906         this.el.setHeight(size);
52907     }
52908 };
52909 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52910     orientation: Roo.SplitBar.VERTICAL,
52911     getBox : function(){
52912         if(this.collapsed){
52913             return this.collapsedEl.getBox();
52914         }
52915         var box = this.el.getBox();
52916         if(this.split){
52917             var sh = this.split.el.getHeight();
52918             box.height += sh;
52919             box.y -= sh;
52920         }
52921         return box;
52922     },
52923     
52924     updateBox : function(box){
52925         if(this.split && !this.collapsed){
52926             var sh = this.split.el.getHeight();
52927             box.height -= sh;
52928             box.y += sh;
52929             this.split.el.setLeft(box.x);
52930             this.split.el.setTop(box.y-sh);
52931             this.split.el.setWidth(box.width);
52932         }
52933         if(this.collapsed){
52934             this.updateBody(box.width, null);
52935         }
52936         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52937     }
52938 });
52939
52940 Roo.EastLayoutRegion = function(mgr, config){
52941     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52942     if(this.split){
52943         this.split.placement = Roo.SplitBar.RIGHT;
52944         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52945         this.split.el.addClass("x-layout-split-h");
52946     }
52947     var size = config.initialSize || config.width;
52948     if(typeof size != "undefined"){
52949         this.el.setWidth(size);
52950     }
52951 };
52952 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52953     orientation: Roo.SplitBar.HORIZONTAL,
52954     getBox : function(){
52955         if(this.collapsed){
52956             return this.collapsedEl.getBox();
52957         }
52958         var box = this.el.getBox();
52959         if(this.split){
52960             var sw = this.split.el.getWidth();
52961             box.width += sw;
52962             box.x -= sw;
52963         }
52964         return box;
52965     },
52966
52967     updateBox : function(box){
52968         if(this.split && !this.collapsed){
52969             var sw = this.split.el.getWidth();
52970             box.width -= sw;
52971             this.split.el.setLeft(box.x);
52972             this.split.el.setTop(box.y);
52973             this.split.el.setHeight(box.height);
52974             box.x += sw;
52975         }
52976         if(this.collapsed){
52977             this.updateBody(null, box.height);
52978         }
52979         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52980     }
52981 });
52982
52983 Roo.WestLayoutRegion = function(mgr, config){
52984     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52985     if(this.split){
52986         this.split.placement = Roo.SplitBar.LEFT;
52987         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52988         this.split.el.addClass("x-layout-split-h");
52989     }
52990     var size = config.initialSize || config.width;
52991     if(typeof size != "undefined"){
52992         this.el.setWidth(size);
52993     }
52994 };
52995 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52996     orientation: Roo.SplitBar.HORIZONTAL,
52997     getBox : function(){
52998         if(this.collapsed){
52999             return this.collapsedEl.getBox();
53000         }
53001         var box = this.el.getBox();
53002         if(this.split){
53003             box.width += this.split.el.getWidth();
53004         }
53005         return box;
53006     },
53007     
53008     updateBox : function(box){
53009         if(this.split && !this.collapsed){
53010             var sw = this.split.el.getWidth();
53011             box.width -= sw;
53012             this.split.el.setLeft(box.x+box.width);
53013             this.split.el.setTop(box.y);
53014             this.split.el.setHeight(box.height);
53015         }
53016         if(this.collapsed){
53017             this.updateBody(null, box.height);
53018         }
53019         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53020     }
53021 });
53022 /*
53023  * Based on:
53024  * Ext JS Library 1.1.1
53025  * Copyright(c) 2006-2007, Ext JS, LLC.
53026  *
53027  * Originally Released Under LGPL - original licence link has changed is not relivant.
53028  *
53029  * Fork - LGPL
53030  * <script type="text/javascript">
53031  */
53032  
53033  
53034 /*
53035  * Private internal class for reading and applying state
53036  */
53037 Roo.LayoutStateManager = function(layout){
53038      // default empty state
53039      this.state = {
53040         north: {},
53041         south: {},
53042         east: {},
53043         west: {}       
53044     };
53045 };
53046
53047 Roo.LayoutStateManager.prototype = {
53048     init : function(layout, provider){
53049         this.provider = provider;
53050         var state = provider.get(layout.id+"-layout-state");
53051         if(state){
53052             var wasUpdating = layout.isUpdating();
53053             if(!wasUpdating){
53054                 layout.beginUpdate();
53055             }
53056             for(var key in state){
53057                 if(typeof state[key] != "function"){
53058                     var rstate = state[key];
53059                     var r = layout.getRegion(key);
53060                     if(r && rstate){
53061                         if(rstate.size){
53062                             r.resizeTo(rstate.size);
53063                         }
53064                         if(rstate.collapsed == true){
53065                             r.collapse(true);
53066                         }else{
53067                             r.expand(null, true);
53068                         }
53069                     }
53070                 }
53071             }
53072             if(!wasUpdating){
53073                 layout.endUpdate();
53074             }
53075             this.state = state; 
53076         }
53077         this.layout = layout;
53078         layout.on("regionresized", this.onRegionResized, this);
53079         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53080         layout.on("regionexpanded", this.onRegionExpanded, this);
53081     },
53082     
53083     storeState : function(){
53084         this.provider.set(this.layout.id+"-layout-state", this.state);
53085     },
53086     
53087     onRegionResized : function(region, newSize){
53088         this.state[region.getPosition()].size = newSize;
53089         this.storeState();
53090     },
53091     
53092     onRegionCollapsed : function(region){
53093         this.state[region.getPosition()].collapsed = true;
53094         this.storeState();
53095     },
53096     
53097     onRegionExpanded : function(region){
53098         this.state[region.getPosition()].collapsed = false;
53099         this.storeState();
53100     }
53101 };/*
53102  * Based on:
53103  * Ext JS Library 1.1.1
53104  * Copyright(c) 2006-2007, Ext JS, LLC.
53105  *
53106  * Originally Released Under LGPL - original licence link has changed is not relivant.
53107  *
53108  * Fork - LGPL
53109  * <script type="text/javascript">
53110  */
53111 /**
53112  * @class Roo.ContentPanel
53113  * @extends Roo.util.Observable
53114  * A basic ContentPanel element.
53115  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53116  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53117  * @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
53118  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53119  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53120  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53121  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53122  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53123  * @cfg {String} title          The title for this panel
53124  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53125  * @cfg {String} url            Calls {@link #setUrl} with this value
53126  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53127  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53128  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53129  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53130
53131  * @constructor
53132  * Create a new ContentPanel.
53133  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53134  * @param {String/Object} config A string to set only the title or a config object
53135  * @param {String} content (optional) Set the HTML content for this panel
53136  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53137  */
53138 Roo.ContentPanel = function(el, config, content){
53139     
53140      
53141     /*
53142     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53143         config = el;
53144         el = Roo.id();
53145     }
53146     if (config && config.parentLayout) { 
53147         el = config.parentLayout.el.createChild(); 
53148     }
53149     */
53150     if(el.autoCreate){ // xtype is available if this is called from factory
53151         config = el;
53152         el = Roo.id();
53153     }
53154     this.el = Roo.get(el);
53155     if(!this.el && config && config.autoCreate){
53156         if(typeof config.autoCreate == "object"){
53157             if(!config.autoCreate.id){
53158                 config.autoCreate.id = config.id||el;
53159             }
53160             this.el = Roo.DomHelper.append(document.body,
53161                         config.autoCreate, true);
53162         }else{
53163             this.el = Roo.DomHelper.append(document.body,
53164                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53165         }
53166     }
53167     this.closable = false;
53168     this.loaded = false;
53169     this.active = false;
53170     if(typeof config == "string"){
53171         this.title = config;
53172     }else{
53173         Roo.apply(this, config);
53174     }
53175     
53176     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53177         this.wrapEl = this.el.wrap();
53178         this.toolbar.container = this.el.insertSibling(false, 'before');
53179         this.toolbar = new Roo.Toolbar(this.toolbar);
53180     }
53181     
53182     // xtype created footer. - not sure if will work as we normally have to render first..
53183     if (this.footer && !this.footer.el && this.footer.xtype) {
53184         if (!this.wrapEl) {
53185             this.wrapEl = this.el.wrap();
53186         }
53187     
53188         this.footer.container = this.wrapEl.createChild();
53189          
53190         this.footer = Roo.factory(this.footer, Roo);
53191         
53192     }
53193     
53194     if(this.resizeEl){
53195         this.resizeEl = Roo.get(this.resizeEl, true);
53196     }else{
53197         this.resizeEl = this.el;
53198     }
53199     // handle view.xtype
53200     
53201  
53202     
53203     
53204     this.addEvents({
53205         /**
53206          * @event activate
53207          * Fires when this panel is activated. 
53208          * @param {Roo.ContentPanel} this
53209          */
53210         "activate" : true,
53211         /**
53212          * @event deactivate
53213          * Fires when this panel is activated. 
53214          * @param {Roo.ContentPanel} this
53215          */
53216         "deactivate" : true,
53217
53218         /**
53219          * @event resize
53220          * Fires when this panel is resized if fitToFrame is true.
53221          * @param {Roo.ContentPanel} this
53222          * @param {Number} width The width after any component adjustments
53223          * @param {Number} height The height after any component adjustments
53224          */
53225         "resize" : true,
53226         
53227          /**
53228          * @event render
53229          * Fires when this tab is created
53230          * @param {Roo.ContentPanel} this
53231          */
53232         "render" : true
53233         
53234         
53235         
53236     });
53237     
53238
53239     
53240     
53241     if(this.autoScroll){
53242         this.resizeEl.setStyle("overflow", "auto");
53243     } else {
53244         // fix randome scrolling
53245         this.el.on('scroll', function() {
53246             Roo.log('fix random scolling');
53247             this.scrollTo('top',0); 
53248         });
53249     }
53250     content = content || this.content;
53251     if(content){
53252         this.setContent(content);
53253     }
53254     if(config && config.url){
53255         this.setUrl(this.url, this.params, this.loadOnce);
53256     }
53257     
53258     
53259     
53260     Roo.ContentPanel.superclass.constructor.call(this);
53261     
53262     if (this.view && typeof(this.view.xtype) != 'undefined') {
53263         this.view.el = this.el.appendChild(document.createElement("div"));
53264         this.view = Roo.factory(this.view); 
53265         this.view.render  &&  this.view.render(false, '');  
53266     }
53267     
53268     
53269     this.fireEvent('render', this);
53270 };
53271
53272 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53273     tabTip:'',
53274     setRegion : function(region){
53275         this.region = region;
53276         if(region){
53277            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53278         }else{
53279            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53280         } 
53281     },
53282     
53283     /**
53284      * Returns the toolbar for this Panel if one was configured. 
53285      * @return {Roo.Toolbar} 
53286      */
53287     getToolbar : function(){
53288         return this.toolbar;
53289     },
53290     
53291     setActiveState : function(active){
53292         this.active = active;
53293         if(!active){
53294             this.fireEvent("deactivate", this);
53295         }else{
53296             this.fireEvent("activate", this);
53297         }
53298     },
53299     /**
53300      * Updates this panel's element
53301      * @param {String} content The new content
53302      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53303     */
53304     setContent : function(content, loadScripts){
53305         this.el.update(content, loadScripts);
53306     },
53307
53308     ignoreResize : function(w, h){
53309         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53310             return true;
53311         }else{
53312             this.lastSize = {width: w, height: h};
53313             return false;
53314         }
53315     },
53316     /**
53317      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53318      * @return {Roo.UpdateManager} The UpdateManager
53319      */
53320     getUpdateManager : function(){
53321         return this.el.getUpdateManager();
53322     },
53323      /**
53324      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53325      * @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:
53326 <pre><code>
53327 panel.load({
53328     url: "your-url.php",
53329     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53330     callback: yourFunction,
53331     scope: yourObject, //(optional scope)
53332     discardUrl: false,
53333     nocache: false,
53334     text: "Loading...",
53335     timeout: 30,
53336     scripts: false
53337 });
53338 </code></pre>
53339      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53340      * 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.
53341      * @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}
53342      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53343      * @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.
53344      * @return {Roo.ContentPanel} this
53345      */
53346     load : function(){
53347         var um = this.el.getUpdateManager();
53348         um.update.apply(um, arguments);
53349         return this;
53350     },
53351
53352
53353     /**
53354      * 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.
53355      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53356      * @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)
53357      * @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)
53358      * @return {Roo.UpdateManager} The UpdateManager
53359      */
53360     setUrl : function(url, params, loadOnce){
53361         if(this.refreshDelegate){
53362             this.removeListener("activate", this.refreshDelegate);
53363         }
53364         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53365         this.on("activate", this.refreshDelegate);
53366         return this.el.getUpdateManager();
53367     },
53368     
53369     _handleRefresh : function(url, params, loadOnce){
53370         if(!loadOnce || !this.loaded){
53371             var updater = this.el.getUpdateManager();
53372             updater.update(url, params, this._setLoaded.createDelegate(this));
53373         }
53374     },
53375     
53376     _setLoaded : function(){
53377         this.loaded = true;
53378     }, 
53379     
53380     /**
53381      * Returns this panel's id
53382      * @return {String} 
53383      */
53384     getId : function(){
53385         return this.el.id;
53386     },
53387     
53388     /** 
53389      * Returns this panel's element - used by regiosn to add.
53390      * @return {Roo.Element} 
53391      */
53392     getEl : function(){
53393         return this.wrapEl || this.el;
53394     },
53395     
53396     adjustForComponents : function(width, height)
53397     {
53398         //Roo.log('adjustForComponents ');
53399         if(this.resizeEl != this.el){
53400             width -= this.el.getFrameWidth('lr');
53401             height -= this.el.getFrameWidth('tb');
53402         }
53403         if(this.toolbar){
53404             var te = this.toolbar.getEl();
53405             height -= te.getHeight();
53406             te.setWidth(width);
53407         }
53408         if(this.footer){
53409             var te = this.footer.getEl();
53410             Roo.log("footer:" + te.getHeight());
53411             
53412             height -= te.getHeight();
53413             te.setWidth(width);
53414         }
53415         
53416         
53417         if(this.adjustments){
53418             width += this.adjustments[0];
53419             height += this.adjustments[1];
53420         }
53421         return {"width": width, "height": height};
53422     },
53423     
53424     setSize : function(width, height){
53425         if(this.fitToFrame && !this.ignoreResize(width, height)){
53426             if(this.fitContainer && this.resizeEl != this.el){
53427                 this.el.setSize(width, height);
53428             }
53429             var size = this.adjustForComponents(width, height);
53430             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53431             this.fireEvent('resize', this, size.width, size.height);
53432         }
53433     },
53434     
53435     /**
53436      * Returns this panel's title
53437      * @return {String} 
53438      */
53439     getTitle : function(){
53440         return this.title;
53441     },
53442     
53443     /**
53444      * Set this panel's title
53445      * @param {String} title
53446      */
53447     setTitle : function(title){
53448         this.title = title;
53449         if(this.region){
53450             this.region.updatePanelTitle(this, title);
53451         }
53452     },
53453     
53454     /**
53455      * Returns true is this panel was configured to be closable
53456      * @return {Boolean} 
53457      */
53458     isClosable : function(){
53459         return this.closable;
53460     },
53461     
53462     beforeSlide : function(){
53463         this.el.clip();
53464         this.resizeEl.clip();
53465     },
53466     
53467     afterSlide : function(){
53468         this.el.unclip();
53469         this.resizeEl.unclip();
53470     },
53471     
53472     /**
53473      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53474      *   Will fail silently if the {@link #setUrl} method has not been called.
53475      *   This does not activate the panel, just updates its content.
53476      */
53477     refresh : function(){
53478         if(this.refreshDelegate){
53479            this.loaded = false;
53480            this.refreshDelegate();
53481         }
53482     },
53483     
53484     /**
53485      * Destroys this panel
53486      */
53487     destroy : function(){
53488         this.el.removeAllListeners();
53489         var tempEl = document.createElement("span");
53490         tempEl.appendChild(this.el.dom);
53491         tempEl.innerHTML = "";
53492         this.el.remove();
53493         this.el = null;
53494     },
53495     
53496     /**
53497      * form - if the content panel contains a form - this is a reference to it.
53498      * @type {Roo.form.Form}
53499      */
53500     form : false,
53501     /**
53502      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53503      *    This contains a reference to it.
53504      * @type {Roo.View}
53505      */
53506     view : false,
53507     
53508       /**
53509      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53510      * <pre><code>
53511
53512 layout.addxtype({
53513        xtype : 'Form',
53514        items: [ .... ]
53515    }
53516 );
53517
53518 </code></pre>
53519      * @param {Object} cfg Xtype definition of item to add.
53520      */
53521     
53522     addxtype : function(cfg) {
53523         // add form..
53524         if (cfg.xtype.match(/^Form$/)) {
53525             
53526             var el;
53527             //if (this.footer) {
53528             //    el = this.footer.container.insertSibling(false, 'before');
53529             //} else {
53530                 el = this.el.createChild();
53531             //}
53532
53533             this.form = new  Roo.form.Form(cfg);
53534             
53535             
53536             if ( this.form.allItems.length) {
53537                 this.form.render(el.dom);
53538             }
53539             return this.form;
53540         }
53541         // should only have one of theses..
53542         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53543             // views.. should not be just added - used named prop 'view''
53544             
53545             cfg.el = this.el.appendChild(document.createElement("div"));
53546             // factory?
53547             
53548             var ret = new Roo.factory(cfg);
53549              
53550              ret.render && ret.render(false, ''); // render blank..
53551             this.view = ret;
53552             return ret;
53553         }
53554         return false;
53555     }
53556 });
53557
53558 /**
53559  * @class Roo.GridPanel
53560  * @extends Roo.ContentPanel
53561  * @constructor
53562  * Create a new GridPanel.
53563  * @param {Roo.grid.Grid} grid The grid for this panel
53564  * @param {String/Object} config A string to set only the panel's title, or a config object
53565  */
53566 Roo.GridPanel = function(grid, config){
53567     
53568   
53569     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53570         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53571         
53572     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53573     
53574     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53575     
53576     if(this.toolbar){
53577         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53578     }
53579     // xtype created footer. - not sure if will work as we normally have to render first..
53580     if (this.footer && !this.footer.el && this.footer.xtype) {
53581         
53582         this.footer.container = this.grid.getView().getFooterPanel(true);
53583         this.footer.dataSource = this.grid.dataSource;
53584         this.footer = Roo.factory(this.footer, Roo);
53585         
53586     }
53587     
53588     grid.monitorWindowResize = false; // turn off autosizing
53589     grid.autoHeight = false;
53590     grid.autoWidth = false;
53591     this.grid = grid;
53592     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53593 };
53594
53595 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53596     getId : function(){
53597         return this.grid.id;
53598     },
53599     
53600     /**
53601      * Returns the grid for this panel
53602      * @return {Roo.grid.Grid} 
53603      */
53604     getGrid : function(){
53605         return this.grid;    
53606     },
53607     
53608     setSize : function(width, height){
53609         if(!this.ignoreResize(width, height)){
53610             var grid = this.grid;
53611             var size = this.adjustForComponents(width, height);
53612             grid.getGridEl().setSize(size.width, size.height);
53613             grid.autoSize();
53614         }
53615     },
53616     
53617     beforeSlide : function(){
53618         this.grid.getView().scroller.clip();
53619     },
53620     
53621     afterSlide : function(){
53622         this.grid.getView().scroller.unclip();
53623     },
53624     
53625     destroy : function(){
53626         this.grid.destroy();
53627         delete this.grid;
53628         Roo.GridPanel.superclass.destroy.call(this); 
53629     }
53630 });
53631
53632
53633 /**
53634  * @class Roo.NestedLayoutPanel
53635  * @extends Roo.ContentPanel
53636  * @constructor
53637  * Create a new NestedLayoutPanel.
53638  * 
53639  * 
53640  * @param {Roo.BorderLayout} layout The layout for this panel
53641  * @param {String/Object} config A string to set only the title or a config object
53642  */
53643 Roo.NestedLayoutPanel = function(layout, config)
53644 {
53645     // construct with only one argument..
53646     /* FIXME - implement nicer consturctors
53647     if (layout.layout) {
53648         config = layout;
53649         layout = config.layout;
53650         delete config.layout;
53651     }
53652     if (layout.xtype && !layout.getEl) {
53653         // then layout needs constructing..
53654         layout = Roo.factory(layout, Roo);
53655     }
53656     */
53657     
53658     
53659     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53660     
53661     layout.monitorWindowResize = false; // turn off autosizing
53662     this.layout = layout;
53663     this.layout.getEl().addClass("x-layout-nested-layout");
53664     
53665     
53666     
53667     
53668 };
53669
53670 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53671
53672     setSize : function(width, height){
53673         if(!this.ignoreResize(width, height)){
53674             var size = this.adjustForComponents(width, height);
53675             var el = this.layout.getEl();
53676             el.setSize(size.width, size.height);
53677             var touch = el.dom.offsetWidth;
53678             this.layout.layout();
53679             // ie requires a double layout on the first pass
53680             if(Roo.isIE && !this.initialized){
53681                 this.initialized = true;
53682                 this.layout.layout();
53683             }
53684         }
53685     },
53686     
53687     // activate all subpanels if not currently active..
53688     
53689     setActiveState : function(active){
53690         this.active = active;
53691         if(!active){
53692             this.fireEvent("deactivate", this);
53693             return;
53694         }
53695         
53696         this.fireEvent("activate", this);
53697         // not sure if this should happen before or after..
53698         if (!this.layout) {
53699             return; // should not happen..
53700         }
53701         var reg = false;
53702         for (var r in this.layout.regions) {
53703             reg = this.layout.getRegion(r);
53704             if (reg.getActivePanel()) {
53705                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53706                 reg.setActivePanel(reg.getActivePanel());
53707                 continue;
53708             }
53709             if (!reg.panels.length) {
53710                 continue;
53711             }
53712             reg.showPanel(reg.getPanel(0));
53713         }
53714         
53715         
53716         
53717         
53718     },
53719     
53720     /**
53721      * Returns the nested BorderLayout for this panel
53722      * @return {Roo.BorderLayout} 
53723      */
53724     getLayout : function(){
53725         return this.layout;
53726     },
53727     
53728      /**
53729      * Adds a xtype elements to the layout of the nested panel
53730      * <pre><code>
53731
53732 panel.addxtype({
53733        xtype : 'ContentPanel',
53734        region: 'west',
53735        items: [ .... ]
53736    }
53737 );
53738
53739 panel.addxtype({
53740         xtype : 'NestedLayoutPanel',
53741         region: 'west',
53742         layout: {
53743            center: { },
53744            west: { }   
53745         },
53746         items : [ ... list of content panels or nested layout panels.. ]
53747    }
53748 );
53749 </code></pre>
53750      * @param {Object} cfg Xtype definition of item to add.
53751      */
53752     addxtype : function(cfg) {
53753         return this.layout.addxtype(cfg);
53754     
53755     }
53756 });
53757
53758 Roo.ScrollPanel = function(el, config, content){
53759     config = config || {};
53760     config.fitToFrame = true;
53761     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53762     
53763     this.el.dom.style.overflow = "hidden";
53764     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53765     this.el.removeClass("x-layout-inactive-content");
53766     this.el.on("mousewheel", this.onWheel, this);
53767
53768     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53769     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53770     up.unselectable(); down.unselectable();
53771     up.on("click", this.scrollUp, this);
53772     down.on("click", this.scrollDown, this);
53773     up.addClassOnOver("x-scroller-btn-over");
53774     down.addClassOnOver("x-scroller-btn-over");
53775     up.addClassOnClick("x-scroller-btn-click");
53776     down.addClassOnClick("x-scroller-btn-click");
53777     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53778
53779     this.resizeEl = this.el;
53780     this.el = wrap; this.up = up; this.down = down;
53781 };
53782
53783 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53784     increment : 100,
53785     wheelIncrement : 5,
53786     scrollUp : function(){
53787         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53788     },
53789
53790     scrollDown : function(){
53791         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53792     },
53793
53794     afterScroll : function(){
53795         var el = this.resizeEl;
53796         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53797         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53798         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53799     },
53800
53801     setSize : function(){
53802         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53803         this.afterScroll();
53804     },
53805
53806     onWheel : function(e){
53807         var d = e.getWheelDelta();
53808         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53809         this.afterScroll();
53810         e.stopEvent();
53811     },
53812
53813     setContent : function(content, loadScripts){
53814         this.resizeEl.update(content, loadScripts);
53815     }
53816
53817 });
53818
53819
53820
53821
53822
53823
53824
53825
53826
53827 /**
53828  * @class Roo.TreePanel
53829  * @extends Roo.ContentPanel
53830  * @constructor
53831  * Create a new TreePanel. - defaults to fit/scoll contents.
53832  * @param {String/Object} config A string to set only the panel's title, or a config object
53833  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53834  */
53835 Roo.TreePanel = function(config){
53836     var el = config.el;
53837     var tree = config.tree;
53838     delete config.tree; 
53839     delete config.el; // hopefull!
53840     
53841     // wrapper for IE7 strict & safari scroll issue
53842     
53843     var treeEl = el.createChild();
53844     config.resizeEl = treeEl;
53845     
53846     
53847     
53848     Roo.TreePanel.superclass.constructor.call(this, el, config);
53849  
53850  
53851     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53852     //console.log(tree);
53853     this.on('activate', function()
53854     {
53855         if (this.tree.rendered) {
53856             return;
53857         }
53858         //console.log('render tree');
53859         this.tree.render();
53860     });
53861     // this should not be needed.. - it's actually the 'el' that resizes?
53862     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53863     
53864     //this.on('resize',  function (cp, w, h) {
53865     //        this.tree.innerCt.setWidth(w);
53866     //        this.tree.innerCt.setHeight(h);
53867     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53868     //});
53869
53870         
53871     
53872 };
53873
53874 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53875     fitToFrame : true,
53876     autoScroll : true
53877 });
53878
53879
53880
53881
53882
53883
53884
53885
53886
53887
53888
53889 /*
53890  * Based on:
53891  * Ext JS Library 1.1.1
53892  * Copyright(c) 2006-2007, Ext JS, LLC.
53893  *
53894  * Originally Released Under LGPL - original licence link has changed is not relivant.
53895  *
53896  * Fork - LGPL
53897  * <script type="text/javascript">
53898  */
53899  
53900
53901 /**
53902  * @class Roo.ReaderLayout
53903  * @extends Roo.BorderLayout
53904  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53905  * center region containing two nested regions (a top one for a list view and one for item preview below),
53906  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53907  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53908  * expedites the setup of the overall layout and regions for this common application style.
53909  * Example:
53910  <pre><code>
53911 var reader = new Roo.ReaderLayout();
53912 var CP = Roo.ContentPanel;  // shortcut for adding
53913
53914 reader.beginUpdate();
53915 reader.add("north", new CP("north", "North"));
53916 reader.add("west", new CP("west", {title: "West"}));
53917 reader.add("east", new CP("east", {title: "East"}));
53918
53919 reader.regions.listView.add(new CP("listView", "List"));
53920 reader.regions.preview.add(new CP("preview", "Preview"));
53921 reader.endUpdate();
53922 </code></pre>
53923 * @constructor
53924 * Create a new ReaderLayout
53925 * @param {Object} config Configuration options
53926 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53927 * document.body if omitted)
53928 */
53929 Roo.ReaderLayout = function(config, renderTo){
53930     var c = config || {size:{}};
53931     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53932         north: c.north !== false ? Roo.apply({
53933             split:false,
53934             initialSize: 32,
53935             titlebar: false
53936         }, c.north) : false,
53937         west: c.west !== false ? Roo.apply({
53938             split:true,
53939             initialSize: 200,
53940             minSize: 175,
53941             maxSize: 400,
53942             titlebar: true,
53943             collapsible: true,
53944             animate: true,
53945             margins:{left:5,right:0,bottom:5,top:5},
53946             cmargins:{left:5,right:5,bottom:5,top:5}
53947         }, c.west) : false,
53948         east: c.east !== false ? Roo.apply({
53949             split:true,
53950             initialSize: 200,
53951             minSize: 175,
53952             maxSize: 400,
53953             titlebar: true,
53954             collapsible: true,
53955             animate: true,
53956             margins:{left:0,right:5,bottom:5,top:5},
53957             cmargins:{left:5,right:5,bottom:5,top:5}
53958         }, c.east) : false,
53959         center: Roo.apply({
53960             tabPosition: 'top',
53961             autoScroll:false,
53962             closeOnTab: true,
53963             titlebar:false,
53964             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53965         }, c.center)
53966     });
53967
53968     this.el.addClass('x-reader');
53969
53970     this.beginUpdate();
53971
53972     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53973         south: c.preview !== false ? Roo.apply({
53974             split:true,
53975             initialSize: 200,
53976             minSize: 100,
53977             autoScroll:true,
53978             collapsible:true,
53979             titlebar: true,
53980             cmargins:{top:5,left:0, right:0, bottom:0}
53981         }, c.preview) : false,
53982         center: Roo.apply({
53983             autoScroll:false,
53984             titlebar:false,
53985             minHeight:200
53986         }, c.listView)
53987     });
53988     this.add('center', new Roo.NestedLayoutPanel(inner,
53989             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53990
53991     this.endUpdate();
53992
53993     this.regions.preview = inner.getRegion('south');
53994     this.regions.listView = inner.getRegion('center');
53995 };
53996
53997 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53998  * Based on:
53999  * Ext JS Library 1.1.1
54000  * Copyright(c) 2006-2007, Ext JS, LLC.
54001  *
54002  * Originally Released Under LGPL - original licence link has changed is not relivant.
54003  *
54004  * Fork - LGPL
54005  * <script type="text/javascript">
54006  */
54007  
54008 /**
54009  * @class Roo.grid.Grid
54010  * @extends Roo.util.Observable
54011  * This class represents the primary interface of a component based grid control.
54012  * <br><br>Usage:<pre><code>
54013  var grid = new Roo.grid.Grid("my-container-id", {
54014      ds: myDataStore,
54015      cm: myColModel,
54016      selModel: mySelectionModel,
54017      autoSizeColumns: true,
54018      monitorWindowResize: false,
54019      trackMouseOver: true
54020  });
54021  // set any options
54022  grid.render();
54023  * </code></pre>
54024  * <b>Common Problems:</b><br/>
54025  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54026  * element will correct this<br/>
54027  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54028  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54029  * are unpredictable.<br/>
54030  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54031  * grid to calculate dimensions/offsets.<br/>
54032   * @constructor
54033  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54034  * The container MUST have some type of size defined for the grid to fill. The container will be
54035  * automatically set to position relative if it isn't already.
54036  * @param {Object} config A config object that sets properties on this grid.
54037  */
54038 Roo.grid.Grid = function(container, config){
54039         // initialize the container
54040         this.container = Roo.get(container);
54041         this.container.update("");
54042         this.container.setStyle("overflow", "hidden");
54043     this.container.addClass('x-grid-container');
54044
54045     this.id = this.container.id;
54046
54047     Roo.apply(this, config);
54048     // check and correct shorthanded configs
54049     if(this.ds){
54050         this.dataSource = this.ds;
54051         delete this.ds;
54052     }
54053     if(this.cm){
54054         this.colModel = this.cm;
54055         delete this.cm;
54056     }
54057     if(this.sm){
54058         this.selModel = this.sm;
54059         delete this.sm;
54060     }
54061
54062     if (this.selModel) {
54063         this.selModel = Roo.factory(this.selModel, Roo.grid);
54064         this.sm = this.selModel;
54065         this.sm.xmodule = this.xmodule || false;
54066     }
54067     if (typeof(this.colModel.config) == 'undefined') {
54068         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54069         this.cm = this.colModel;
54070         this.cm.xmodule = this.xmodule || false;
54071     }
54072     if (this.dataSource) {
54073         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54074         this.ds = this.dataSource;
54075         this.ds.xmodule = this.xmodule || false;
54076          
54077     }
54078     
54079     
54080     
54081     if(this.width){
54082         this.container.setWidth(this.width);
54083     }
54084
54085     if(this.height){
54086         this.container.setHeight(this.height);
54087     }
54088     /** @private */
54089         this.addEvents({
54090         // raw events
54091         /**
54092          * @event click
54093          * The raw click event for the entire grid.
54094          * @param {Roo.EventObject} e
54095          */
54096         "click" : true,
54097         /**
54098          * @event dblclick
54099          * The raw dblclick event for the entire grid.
54100          * @param {Roo.EventObject} e
54101          */
54102         "dblclick" : true,
54103         /**
54104          * @event contextmenu
54105          * The raw contextmenu event for the entire grid.
54106          * @param {Roo.EventObject} e
54107          */
54108         "contextmenu" : true,
54109         /**
54110          * @event mousedown
54111          * The raw mousedown event for the entire grid.
54112          * @param {Roo.EventObject} e
54113          */
54114         "mousedown" : true,
54115         /**
54116          * @event mouseup
54117          * The raw mouseup event for the entire grid.
54118          * @param {Roo.EventObject} e
54119          */
54120         "mouseup" : true,
54121         /**
54122          * @event mouseover
54123          * The raw mouseover event for the entire grid.
54124          * @param {Roo.EventObject} e
54125          */
54126         "mouseover" : true,
54127         /**
54128          * @event mouseout
54129          * The raw mouseout event for the entire grid.
54130          * @param {Roo.EventObject} e
54131          */
54132         "mouseout" : true,
54133         /**
54134          * @event keypress
54135          * The raw keypress event for the entire grid.
54136          * @param {Roo.EventObject} e
54137          */
54138         "keypress" : true,
54139         /**
54140          * @event keydown
54141          * The raw keydown event for the entire grid.
54142          * @param {Roo.EventObject} e
54143          */
54144         "keydown" : true,
54145
54146         // custom events
54147
54148         /**
54149          * @event cellclick
54150          * Fires when a cell is clicked
54151          * @param {Grid} this
54152          * @param {Number} rowIndex
54153          * @param {Number} columnIndex
54154          * @param {Roo.EventObject} e
54155          */
54156         "cellclick" : true,
54157         /**
54158          * @event celldblclick
54159          * Fires when a cell is double clicked
54160          * @param {Grid} this
54161          * @param {Number} rowIndex
54162          * @param {Number} columnIndex
54163          * @param {Roo.EventObject} e
54164          */
54165         "celldblclick" : true,
54166         /**
54167          * @event rowclick
54168          * Fires when a row is clicked
54169          * @param {Grid} this
54170          * @param {Number} rowIndex
54171          * @param {Roo.EventObject} e
54172          */
54173         "rowclick" : true,
54174         /**
54175          * @event rowdblclick
54176          * Fires when a row is double clicked
54177          * @param {Grid} this
54178          * @param {Number} rowIndex
54179          * @param {Roo.EventObject} e
54180          */
54181         "rowdblclick" : true,
54182         /**
54183          * @event headerclick
54184          * Fires when a header is clicked
54185          * @param {Grid} this
54186          * @param {Number} columnIndex
54187          * @param {Roo.EventObject} e
54188          */
54189         "headerclick" : true,
54190         /**
54191          * @event headerdblclick
54192          * Fires when a header cell is double clicked
54193          * @param {Grid} this
54194          * @param {Number} columnIndex
54195          * @param {Roo.EventObject} e
54196          */
54197         "headerdblclick" : true,
54198         /**
54199          * @event rowcontextmenu
54200          * Fires when a row is right clicked
54201          * @param {Grid} this
54202          * @param {Number} rowIndex
54203          * @param {Roo.EventObject} e
54204          */
54205         "rowcontextmenu" : true,
54206         /**
54207          * @event cellcontextmenu
54208          * Fires when a cell is right clicked
54209          * @param {Grid} this
54210          * @param {Number} rowIndex
54211          * @param {Number} cellIndex
54212          * @param {Roo.EventObject} e
54213          */
54214          "cellcontextmenu" : true,
54215         /**
54216          * @event headercontextmenu
54217          * Fires when a header is right clicked
54218          * @param {Grid} this
54219          * @param {Number} columnIndex
54220          * @param {Roo.EventObject} e
54221          */
54222         "headercontextmenu" : true,
54223         /**
54224          * @event bodyscroll
54225          * Fires when the body element is scrolled
54226          * @param {Number} scrollLeft
54227          * @param {Number} scrollTop
54228          */
54229         "bodyscroll" : true,
54230         /**
54231          * @event columnresize
54232          * Fires when the user resizes a column
54233          * @param {Number} columnIndex
54234          * @param {Number} newSize
54235          */
54236         "columnresize" : true,
54237         /**
54238          * @event columnmove
54239          * Fires when the user moves a column
54240          * @param {Number} oldIndex
54241          * @param {Number} newIndex
54242          */
54243         "columnmove" : true,
54244         /**
54245          * @event startdrag
54246          * Fires when row(s) start being dragged
54247          * @param {Grid} this
54248          * @param {Roo.GridDD} dd The drag drop object
54249          * @param {event} e The raw browser event
54250          */
54251         "startdrag" : true,
54252         /**
54253          * @event enddrag
54254          * Fires when a drag operation is complete
54255          * @param {Grid} this
54256          * @param {Roo.GridDD} dd The drag drop object
54257          * @param {event} e The raw browser event
54258          */
54259         "enddrag" : true,
54260         /**
54261          * @event dragdrop
54262          * Fires when dragged row(s) are dropped on a valid DD target
54263          * @param {Grid} this
54264          * @param {Roo.GridDD} dd The drag drop object
54265          * @param {String} targetId The target drag drop object
54266          * @param {event} e The raw browser event
54267          */
54268         "dragdrop" : true,
54269         /**
54270          * @event dragover
54271          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54272          * @param {Grid} this
54273          * @param {Roo.GridDD} dd The drag drop object
54274          * @param {String} targetId The target drag drop object
54275          * @param {event} e The raw browser event
54276          */
54277         "dragover" : true,
54278         /**
54279          * @event dragenter
54280          *  Fires when the dragged row(s) first cross another DD target while being dragged
54281          * @param {Grid} this
54282          * @param {Roo.GridDD} dd The drag drop object
54283          * @param {String} targetId The target drag drop object
54284          * @param {event} e The raw browser event
54285          */
54286         "dragenter" : true,
54287         /**
54288          * @event dragout
54289          * Fires when the dragged row(s) leave another DD target while being dragged
54290          * @param {Grid} this
54291          * @param {Roo.GridDD} dd The drag drop object
54292          * @param {String} targetId The target drag drop object
54293          * @param {event} e The raw browser event
54294          */
54295         "dragout" : true,
54296         /**
54297          * @event rowclass
54298          * Fires when a row is rendered, so you can change add a style to it.
54299          * @param {GridView} gridview   The grid view
54300          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54301          */
54302         'rowclass' : true,
54303
54304         /**
54305          * @event render
54306          * Fires when the grid is rendered
54307          * @param {Grid} grid
54308          */
54309         'render' : true
54310     });
54311
54312     Roo.grid.Grid.superclass.constructor.call(this);
54313 };
54314 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54315     
54316     /**
54317      * @cfg {String} ddGroup - drag drop group.
54318      */
54319
54320     /**
54321      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54322      */
54323     minColumnWidth : 25,
54324
54325     /**
54326      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54327      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54328      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54329      */
54330     autoSizeColumns : false,
54331
54332     /**
54333      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54334      */
54335     autoSizeHeaders : true,
54336
54337     /**
54338      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54339      */
54340     monitorWindowResize : true,
54341
54342     /**
54343      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54344      * rows measured to get a columns size. Default is 0 (all rows).
54345      */
54346     maxRowsToMeasure : 0,
54347
54348     /**
54349      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54350      */
54351     trackMouseOver : true,
54352
54353     /**
54354     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54355     */
54356     
54357     /**
54358     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54359     */
54360     enableDragDrop : false,
54361     
54362     /**
54363     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54364     */
54365     enableColumnMove : true,
54366     
54367     /**
54368     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54369     */
54370     enableColumnHide : true,
54371     
54372     /**
54373     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54374     */
54375     enableRowHeightSync : false,
54376     
54377     /**
54378     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54379     */
54380     stripeRows : true,
54381     
54382     /**
54383     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54384     */
54385     autoHeight : false,
54386
54387     /**
54388      * @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.
54389      */
54390     autoExpandColumn : false,
54391
54392     /**
54393     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54394     * Default is 50.
54395     */
54396     autoExpandMin : 50,
54397
54398     /**
54399     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54400     */
54401     autoExpandMax : 1000,
54402
54403     /**
54404     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54405     */
54406     view : null,
54407
54408     /**
54409     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54410     */
54411     loadMask : false,
54412     /**
54413     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54414     */
54415     dropTarget: false,
54416     
54417    
54418     
54419     // private
54420     rendered : false,
54421
54422     /**
54423     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54424     * of a fixed width. Default is false.
54425     */
54426     /**
54427     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54428     */
54429     /**
54430      * Called once after all setup has been completed and the grid is ready to be rendered.
54431      * @return {Roo.grid.Grid} this
54432      */
54433     render : function()
54434     {
54435         var c = this.container;
54436         // try to detect autoHeight/width mode
54437         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54438             this.autoHeight = true;
54439         }
54440         var view = this.getView();
54441         view.init(this);
54442
54443         c.on("click", this.onClick, this);
54444         c.on("dblclick", this.onDblClick, this);
54445         c.on("contextmenu", this.onContextMenu, this);
54446         c.on("keydown", this.onKeyDown, this);
54447         if (Roo.isTouch) {
54448             c.on("touchstart", this.onTouchStart, this);
54449         }
54450
54451         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54452
54453         this.getSelectionModel().init(this);
54454
54455         view.render();
54456
54457         if(this.loadMask){
54458             this.loadMask = new Roo.LoadMask(this.container,
54459                     Roo.apply({store:this.dataSource}, this.loadMask));
54460         }
54461         
54462         
54463         if (this.toolbar && this.toolbar.xtype) {
54464             this.toolbar.container = this.getView().getHeaderPanel(true);
54465             this.toolbar = new Roo.Toolbar(this.toolbar);
54466         }
54467         if (this.footer && this.footer.xtype) {
54468             this.footer.dataSource = this.getDataSource();
54469             this.footer.container = this.getView().getFooterPanel(true);
54470             this.footer = Roo.factory(this.footer, Roo);
54471         }
54472         if (this.dropTarget && this.dropTarget.xtype) {
54473             delete this.dropTarget.xtype;
54474             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54475         }
54476         
54477         
54478         this.rendered = true;
54479         this.fireEvent('render', this);
54480         return this;
54481     },
54482
54483         /**
54484          * Reconfigures the grid to use a different Store and Column Model.
54485          * The View will be bound to the new objects and refreshed.
54486          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54487          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54488          */
54489     reconfigure : function(dataSource, colModel){
54490         if(this.loadMask){
54491             this.loadMask.destroy();
54492             this.loadMask = new Roo.LoadMask(this.container,
54493                     Roo.apply({store:dataSource}, this.loadMask));
54494         }
54495         this.view.bind(dataSource, colModel);
54496         this.dataSource = dataSource;
54497         this.colModel = colModel;
54498         this.view.refresh(true);
54499     },
54500
54501     // private
54502     onKeyDown : function(e){
54503         this.fireEvent("keydown", e);
54504     },
54505
54506     /**
54507      * Destroy this grid.
54508      * @param {Boolean} removeEl True to remove the element
54509      */
54510     destroy : function(removeEl, keepListeners){
54511         if(this.loadMask){
54512             this.loadMask.destroy();
54513         }
54514         var c = this.container;
54515         c.removeAllListeners();
54516         this.view.destroy();
54517         this.colModel.purgeListeners();
54518         if(!keepListeners){
54519             this.purgeListeners();
54520         }
54521         c.update("");
54522         if(removeEl === true){
54523             c.remove();
54524         }
54525     },
54526
54527     // private
54528     processEvent : function(name, e){
54529         // does this fire select???
54530         //Roo.log('grid:processEvent '  + name);
54531         
54532         if (name != 'touchstart' ) {
54533             this.fireEvent(name, e);    
54534         }
54535         
54536         var t = e.getTarget();
54537         var v = this.view;
54538         var header = v.findHeaderIndex(t);
54539         if(header !== false){
54540             var ename = name == 'touchstart' ? 'click' : name;
54541              
54542             this.fireEvent("header" + ename, this, header, e);
54543         }else{
54544             var row = v.findRowIndex(t);
54545             var cell = v.findCellIndex(t);
54546             if (name == 'touchstart') {
54547                 // first touch is always a click.
54548                 // hopefull this happens after selection is updated.?
54549                 name = false;
54550                 
54551                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54552                     var cs = this.selModel.getSelectedCell();
54553                     if (row == cs[0] && cell == cs[1]){
54554                         name = 'dblclick';
54555                     }
54556                 }
54557                 if (typeof(this.selModel.getSelections) != 'undefined') {
54558                     var cs = this.selModel.getSelections();
54559                     var ds = this.dataSource;
54560                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54561                         name = 'dblclick';
54562                     }
54563                 }
54564                 if (!name) {
54565                     return;
54566                 }
54567             }
54568             
54569             
54570             if(row !== false){
54571                 this.fireEvent("row" + name, this, row, e);
54572                 if(cell !== false){
54573                     this.fireEvent("cell" + name, this, row, cell, e);
54574                 }
54575             }
54576         }
54577     },
54578
54579     // private
54580     onClick : function(e){
54581         this.processEvent("click", e);
54582     },
54583    // private
54584     onTouchStart : function(e){
54585         this.processEvent("touchstart", e);
54586     },
54587
54588     // private
54589     onContextMenu : function(e, t){
54590         this.processEvent("contextmenu", e);
54591     },
54592
54593     // private
54594     onDblClick : function(e){
54595         this.processEvent("dblclick", e);
54596     },
54597
54598     // private
54599     walkCells : function(row, col, step, fn, scope){
54600         var cm = this.colModel, clen = cm.getColumnCount();
54601         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54602         if(step < 0){
54603             if(col < 0){
54604                 row--;
54605                 first = false;
54606             }
54607             while(row >= 0){
54608                 if(!first){
54609                     col = clen-1;
54610                 }
54611                 first = false;
54612                 while(col >= 0){
54613                     if(fn.call(scope || this, row, col, cm) === true){
54614                         return [row, col];
54615                     }
54616                     col--;
54617                 }
54618                 row--;
54619             }
54620         } else {
54621             if(col >= clen){
54622                 row++;
54623                 first = false;
54624             }
54625             while(row < rlen){
54626                 if(!first){
54627                     col = 0;
54628                 }
54629                 first = false;
54630                 while(col < clen){
54631                     if(fn.call(scope || this, row, col, cm) === true){
54632                         return [row, col];
54633                     }
54634                     col++;
54635                 }
54636                 row++;
54637             }
54638         }
54639         return null;
54640     },
54641
54642     // private
54643     getSelections : function(){
54644         return this.selModel.getSelections();
54645     },
54646
54647     /**
54648      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54649      * but if manual update is required this method will initiate it.
54650      */
54651     autoSize : function(){
54652         if(this.rendered){
54653             this.view.layout();
54654             if(this.view.adjustForScroll){
54655                 this.view.adjustForScroll();
54656             }
54657         }
54658     },
54659
54660     /**
54661      * Returns the grid's underlying element.
54662      * @return {Element} The element
54663      */
54664     getGridEl : function(){
54665         return this.container;
54666     },
54667
54668     // private for compatibility, overridden by editor grid
54669     stopEditing : function(){},
54670
54671     /**
54672      * Returns the grid's SelectionModel.
54673      * @return {SelectionModel}
54674      */
54675     getSelectionModel : function(){
54676         if(!this.selModel){
54677             this.selModel = new Roo.grid.RowSelectionModel();
54678         }
54679         return this.selModel;
54680     },
54681
54682     /**
54683      * Returns the grid's DataSource.
54684      * @return {DataSource}
54685      */
54686     getDataSource : function(){
54687         return this.dataSource;
54688     },
54689
54690     /**
54691      * Returns the grid's ColumnModel.
54692      * @return {ColumnModel}
54693      */
54694     getColumnModel : function(){
54695         return this.colModel;
54696     },
54697
54698     /**
54699      * Returns the grid's GridView object.
54700      * @return {GridView}
54701      */
54702     getView : function(){
54703         if(!this.view){
54704             this.view = new Roo.grid.GridView(this.viewConfig);
54705         }
54706         return this.view;
54707     },
54708     /**
54709      * Called to get grid's drag proxy text, by default returns this.ddText.
54710      * @return {String}
54711      */
54712     getDragDropText : function(){
54713         var count = this.selModel.getCount();
54714         return String.format(this.ddText, count, count == 1 ? '' : 's');
54715     }
54716 });
54717 /**
54718  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54719  * %0 is replaced with the number of selected rows.
54720  * @type String
54721  */
54722 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54723  * Based on:
54724  * Ext JS Library 1.1.1
54725  * Copyright(c) 2006-2007, Ext JS, LLC.
54726  *
54727  * Originally Released Under LGPL - original licence link has changed is not relivant.
54728  *
54729  * Fork - LGPL
54730  * <script type="text/javascript">
54731  */
54732  
54733 Roo.grid.AbstractGridView = function(){
54734         this.grid = null;
54735         
54736         this.events = {
54737             "beforerowremoved" : true,
54738             "beforerowsinserted" : true,
54739             "beforerefresh" : true,
54740             "rowremoved" : true,
54741             "rowsinserted" : true,
54742             "rowupdated" : true,
54743             "refresh" : true
54744         };
54745     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54746 };
54747
54748 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54749     rowClass : "x-grid-row",
54750     cellClass : "x-grid-cell",
54751     tdClass : "x-grid-td",
54752     hdClass : "x-grid-hd",
54753     splitClass : "x-grid-hd-split",
54754     
54755     init: function(grid){
54756         this.grid = grid;
54757                 var cid = this.grid.getGridEl().id;
54758         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54759         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54760         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54761         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54762         },
54763         
54764     getColumnRenderers : function(){
54765         var renderers = [];
54766         var cm = this.grid.colModel;
54767         var colCount = cm.getColumnCount();
54768         for(var i = 0; i < colCount; i++){
54769             renderers[i] = cm.getRenderer(i);
54770         }
54771         return renderers;
54772     },
54773     
54774     getColumnIds : function(){
54775         var ids = [];
54776         var cm = this.grid.colModel;
54777         var colCount = cm.getColumnCount();
54778         for(var i = 0; i < colCount; i++){
54779             ids[i] = cm.getColumnId(i);
54780         }
54781         return ids;
54782     },
54783     
54784     getDataIndexes : function(){
54785         if(!this.indexMap){
54786             this.indexMap = this.buildIndexMap();
54787         }
54788         return this.indexMap.colToData;
54789     },
54790     
54791     getColumnIndexByDataIndex : function(dataIndex){
54792         if(!this.indexMap){
54793             this.indexMap = this.buildIndexMap();
54794         }
54795         return this.indexMap.dataToCol[dataIndex];
54796     },
54797     
54798     /**
54799      * Set a css style for a column dynamically. 
54800      * @param {Number} colIndex The index of the column
54801      * @param {String} name The css property name
54802      * @param {String} value The css value
54803      */
54804     setCSSStyle : function(colIndex, name, value){
54805         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54806         Roo.util.CSS.updateRule(selector, name, value);
54807     },
54808     
54809     generateRules : function(cm){
54810         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54811         Roo.util.CSS.removeStyleSheet(rulesId);
54812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54813             var cid = cm.getColumnId(i);
54814             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54815                          this.tdSelector, cid, " {\n}\n",
54816                          this.hdSelector, cid, " {\n}\n",
54817                          this.splitSelector, cid, " {\n}\n");
54818         }
54819         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54820     }
54821 });/*
54822  * Based on:
54823  * Ext JS Library 1.1.1
54824  * Copyright(c) 2006-2007, Ext JS, LLC.
54825  *
54826  * Originally Released Under LGPL - original licence link has changed is not relivant.
54827  *
54828  * Fork - LGPL
54829  * <script type="text/javascript">
54830  */
54831
54832 // private
54833 // This is a support class used internally by the Grid components
54834 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54835     this.grid = grid;
54836     this.view = grid.getView();
54837     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54838     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54839     if(hd2){
54840         this.setHandleElId(Roo.id(hd));
54841         this.setOuterHandleElId(Roo.id(hd2));
54842     }
54843     this.scroll = false;
54844 };
54845 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54846     maxDragWidth: 120,
54847     getDragData : function(e){
54848         var t = Roo.lib.Event.getTarget(e);
54849         var h = this.view.findHeaderCell(t);
54850         if(h){
54851             return {ddel: h.firstChild, header:h};
54852         }
54853         return false;
54854     },
54855
54856     onInitDrag : function(e){
54857         this.view.headersDisabled = true;
54858         var clone = this.dragData.ddel.cloneNode(true);
54859         clone.id = Roo.id();
54860         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54861         this.proxy.update(clone);
54862         return true;
54863     },
54864
54865     afterValidDrop : function(){
54866         var v = this.view;
54867         setTimeout(function(){
54868             v.headersDisabled = false;
54869         }, 50);
54870     },
54871
54872     afterInvalidDrop : function(){
54873         var v = this.view;
54874         setTimeout(function(){
54875             v.headersDisabled = false;
54876         }, 50);
54877     }
54878 });
54879 /*
54880  * Based on:
54881  * Ext JS Library 1.1.1
54882  * Copyright(c) 2006-2007, Ext JS, LLC.
54883  *
54884  * Originally Released Under LGPL - original licence link has changed is not relivant.
54885  *
54886  * Fork - LGPL
54887  * <script type="text/javascript">
54888  */
54889 // private
54890 // This is a support class used internally by the Grid components
54891 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54892     this.grid = grid;
54893     this.view = grid.getView();
54894     // split the proxies so they don't interfere with mouse events
54895     this.proxyTop = Roo.DomHelper.append(document.body, {
54896         cls:"col-move-top", html:"&#160;"
54897     }, true);
54898     this.proxyBottom = Roo.DomHelper.append(document.body, {
54899         cls:"col-move-bottom", html:"&#160;"
54900     }, true);
54901     this.proxyTop.hide = this.proxyBottom.hide = function(){
54902         this.setLeftTop(-100,-100);
54903         this.setStyle("visibility", "hidden");
54904     };
54905     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54906     // temporarily disabled
54907     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54908     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54909 };
54910 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54911     proxyOffsets : [-4, -9],
54912     fly: Roo.Element.fly,
54913
54914     getTargetFromEvent : function(e){
54915         var t = Roo.lib.Event.getTarget(e);
54916         var cindex = this.view.findCellIndex(t);
54917         if(cindex !== false){
54918             return this.view.getHeaderCell(cindex);
54919         }
54920         return null;
54921     },
54922
54923     nextVisible : function(h){
54924         var v = this.view, cm = this.grid.colModel;
54925         h = h.nextSibling;
54926         while(h){
54927             if(!cm.isHidden(v.getCellIndex(h))){
54928                 return h;
54929             }
54930             h = h.nextSibling;
54931         }
54932         return null;
54933     },
54934
54935     prevVisible : function(h){
54936         var v = this.view, cm = this.grid.colModel;
54937         h = h.prevSibling;
54938         while(h){
54939             if(!cm.isHidden(v.getCellIndex(h))){
54940                 return h;
54941             }
54942             h = h.prevSibling;
54943         }
54944         return null;
54945     },
54946
54947     positionIndicator : function(h, n, e){
54948         var x = Roo.lib.Event.getPageX(e);
54949         var r = Roo.lib.Dom.getRegion(n.firstChild);
54950         var px, pt, py = r.top + this.proxyOffsets[1];
54951         if((r.right - x) <= (r.right-r.left)/2){
54952             px = r.right+this.view.borderWidth;
54953             pt = "after";
54954         }else{
54955             px = r.left;
54956             pt = "before";
54957         }
54958         var oldIndex = this.view.getCellIndex(h);
54959         var newIndex = this.view.getCellIndex(n);
54960
54961         if(this.grid.colModel.isFixed(newIndex)){
54962             return false;
54963         }
54964
54965         var locked = this.grid.colModel.isLocked(newIndex);
54966
54967         if(pt == "after"){
54968             newIndex++;
54969         }
54970         if(oldIndex < newIndex){
54971             newIndex--;
54972         }
54973         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54974             return false;
54975         }
54976         px +=  this.proxyOffsets[0];
54977         this.proxyTop.setLeftTop(px, py);
54978         this.proxyTop.show();
54979         if(!this.bottomOffset){
54980             this.bottomOffset = this.view.mainHd.getHeight();
54981         }
54982         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54983         this.proxyBottom.show();
54984         return pt;
54985     },
54986
54987     onNodeEnter : function(n, dd, e, data){
54988         if(data.header != n){
54989             this.positionIndicator(data.header, n, e);
54990         }
54991     },
54992
54993     onNodeOver : function(n, dd, e, data){
54994         var result = false;
54995         if(data.header != n){
54996             result = this.positionIndicator(data.header, n, e);
54997         }
54998         if(!result){
54999             this.proxyTop.hide();
55000             this.proxyBottom.hide();
55001         }
55002         return result ? this.dropAllowed : this.dropNotAllowed;
55003     },
55004
55005     onNodeOut : function(n, dd, e, data){
55006         this.proxyTop.hide();
55007         this.proxyBottom.hide();
55008     },
55009
55010     onNodeDrop : function(n, dd, e, data){
55011         var h = data.header;
55012         if(h != n){
55013             var cm = this.grid.colModel;
55014             var x = Roo.lib.Event.getPageX(e);
55015             var r = Roo.lib.Dom.getRegion(n.firstChild);
55016             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55017             var oldIndex = this.view.getCellIndex(h);
55018             var newIndex = this.view.getCellIndex(n);
55019             var locked = cm.isLocked(newIndex);
55020             if(pt == "after"){
55021                 newIndex++;
55022             }
55023             if(oldIndex < newIndex){
55024                 newIndex--;
55025             }
55026             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55027                 return false;
55028             }
55029             cm.setLocked(oldIndex, locked, true);
55030             cm.moveColumn(oldIndex, newIndex);
55031             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55032             return true;
55033         }
55034         return false;
55035     }
55036 });
55037 /*
55038  * Based on:
55039  * Ext JS Library 1.1.1
55040  * Copyright(c) 2006-2007, Ext JS, LLC.
55041  *
55042  * Originally Released Under LGPL - original licence link has changed is not relivant.
55043  *
55044  * Fork - LGPL
55045  * <script type="text/javascript">
55046  */
55047   
55048 /**
55049  * @class Roo.grid.GridView
55050  * @extends Roo.util.Observable
55051  *
55052  * @constructor
55053  * @param {Object} config
55054  */
55055 Roo.grid.GridView = function(config){
55056     Roo.grid.GridView.superclass.constructor.call(this);
55057     this.el = null;
55058
55059     Roo.apply(this, config);
55060 };
55061
55062 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55063
55064     unselectable :  'unselectable="on"',
55065     unselectableCls :  'x-unselectable',
55066     
55067     
55068     rowClass : "x-grid-row",
55069
55070     cellClass : "x-grid-col",
55071
55072     tdClass : "x-grid-td",
55073
55074     hdClass : "x-grid-hd",
55075
55076     splitClass : "x-grid-split",
55077
55078     sortClasses : ["sort-asc", "sort-desc"],
55079
55080     enableMoveAnim : false,
55081
55082     hlColor: "C3DAF9",
55083
55084     dh : Roo.DomHelper,
55085
55086     fly : Roo.Element.fly,
55087
55088     css : Roo.util.CSS,
55089
55090     borderWidth: 1,
55091
55092     splitOffset: 3,
55093
55094     scrollIncrement : 22,
55095
55096     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55097
55098     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55099
55100     bind : function(ds, cm){
55101         if(this.ds){
55102             this.ds.un("load", this.onLoad, this);
55103             this.ds.un("datachanged", this.onDataChange, this);
55104             this.ds.un("add", this.onAdd, this);
55105             this.ds.un("remove", this.onRemove, this);
55106             this.ds.un("update", this.onUpdate, this);
55107             this.ds.un("clear", this.onClear, this);
55108         }
55109         if(ds){
55110             ds.on("load", this.onLoad, this);
55111             ds.on("datachanged", this.onDataChange, this);
55112             ds.on("add", this.onAdd, this);
55113             ds.on("remove", this.onRemove, this);
55114             ds.on("update", this.onUpdate, this);
55115             ds.on("clear", this.onClear, this);
55116         }
55117         this.ds = ds;
55118
55119         if(this.cm){
55120             this.cm.un("widthchange", this.onColWidthChange, this);
55121             this.cm.un("headerchange", this.onHeaderChange, this);
55122             this.cm.un("hiddenchange", this.onHiddenChange, this);
55123             this.cm.un("columnmoved", this.onColumnMove, this);
55124             this.cm.un("columnlockchange", this.onColumnLock, this);
55125         }
55126         if(cm){
55127             this.generateRules(cm);
55128             cm.on("widthchange", this.onColWidthChange, this);
55129             cm.on("headerchange", this.onHeaderChange, this);
55130             cm.on("hiddenchange", this.onHiddenChange, this);
55131             cm.on("columnmoved", this.onColumnMove, this);
55132             cm.on("columnlockchange", this.onColumnLock, this);
55133         }
55134         this.cm = cm;
55135     },
55136
55137     init: function(grid){
55138         Roo.grid.GridView.superclass.init.call(this, grid);
55139
55140         this.bind(grid.dataSource, grid.colModel);
55141
55142         grid.on("headerclick", this.handleHeaderClick, this);
55143
55144         if(grid.trackMouseOver){
55145             grid.on("mouseover", this.onRowOver, this);
55146             grid.on("mouseout", this.onRowOut, this);
55147         }
55148         grid.cancelTextSelection = function(){};
55149         this.gridId = grid.id;
55150
55151         var tpls = this.templates || {};
55152
55153         if(!tpls.master){
55154             tpls.master = new Roo.Template(
55155                '<div class="x-grid" hidefocus="true">',
55156                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55157                   '<div class="x-grid-topbar"></div>',
55158                   '<div class="x-grid-scroller"><div></div></div>',
55159                   '<div class="x-grid-locked">',
55160                       '<div class="x-grid-header">{lockedHeader}</div>',
55161                       '<div class="x-grid-body">{lockedBody}</div>',
55162                   "</div>",
55163                   '<div class="x-grid-viewport">',
55164                       '<div class="x-grid-header">{header}</div>',
55165                       '<div class="x-grid-body">{body}</div>',
55166                   "</div>",
55167                   '<div class="x-grid-bottombar"></div>',
55168                  
55169                   '<div class="x-grid-resize-proxy">&#160;</div>',
55170                "</div>"
55171             );
55172             tpls.master.disableformats = true;
55173         }
55174
55175         if(!tpls.header){
55176             tpls.header = new Roo.Template(
55177                '<table border="0" cellspacing="0" cellpadding="0">',
55178                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55179                "</table>{splits}"
55180             );
55181             tpls.header.disableformats = true;
55182         }
55183         tpls.header.compile();
55184
55185         if(!tpls.hcell){
55186             tpls.hcell = new Roo.Template(
55187                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55188                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55189                 "</div></td>"
55190              );
55191              tpls.hcell.disableFormats = true;
55192         }
55193         tpls.hcell.compile();
55194
55195         if(!tpls.hsplit){
55196             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55197                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55198             tpls.hsplit.disableFormats = true;
55199         }
55200         tpls.hsplit.compile();
55201
55202         if(!tpls.body){
55203             tpls.body = new Roo.Template(
55204                '<table border="0" cellspacing="0" cellpadding="0">',
55205                "<tbody>{rows}</tbody>",
55206                "</table>"
55207             );
55208             tpls.body.disableFormats = true;
55209         }
55210         tpls.body.compile();
55211
55212         if(!tpls.row){
55213             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55214             tpls.row.disableFormats = true;
55215         }
55216         tpls.row.compile();
55217
55218         if(!tpls.cell){
55219             tpls.cell = new Roo.Template(
55220                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55221                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55222                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55223                 "</td>"
55224             );
55225             tpls.cell.disableFormats = true;
55226         }
55227         tpls.cell.compile();
55228
55229         this.templates = tpls;
55230     },
55231
55232     // remap these for backwards compat
55233     onColWidthChange : function(){
55234         this.updateColumns.apply(this, arguments);
55235     },
55236     onHeaderChange : function(){
55237         this.updateHeaders.apply(this, arguments);
55238     }, 
55239     onHiddenChange : function(){
55240         this.handleHiddenChange.apply(this, arguments);
55241     },
55242     onColumnMove : function(){
55243         this.handleColumnMove.apply(this, arguments);
55244     },
55245     onColumnLock : function(){
55246         this.handleLockChange.apply(this, arguments);
55247     },
55248
55249     onDataChange : function(){
55250         this.refresh();
55251         this.updateHeaderSortState();
55252     },
55253
55254     onClear : function(){
55255         this.refresh();
55256     },
55257
55258     onUpdate : function(ds, record){
55259         this.refreshRow(record);
55260     },
55261
55262     refreshRow : function(record){
55263         var ds = this.ds, index;
55264         if(typeof record == 'number'){
55265             index = record;
55266             record = ds.getAt(index);
55267         }else{
55268             index = ds.indexOf(record);
55269         }
55270         this.insertRows(ds, index, index, true);
55271         this.onRemove(ds, record, index+1, true);
55272         this.syncRowHeights(index, index);
55273         this.layout();
55274         this.fireEvent("rowupdated", this, index, record);
55275     },
55276
55277     onAdd : function(ds, records, index){
55278         this.insertRows(ds, index, index + (records.length-1));
55279     },
55280
55281     onRemove : function(ds, record, index, isUpdate){
55282         if(isUpdate !== true){
55283             this.fireEvent("beforerowremoved", this, index, record);
55284         }
55285         var bt = this.getBodyTable(), lt = this.getLockedTable();
55286         if(bt.rows[index]){
55287             bt.firstChild.removeChild(bt.rows[index]);
55288         }
55289         if(lt.rows[index]){
55290             lt.firstChild.removeChild(lt.rows[index]);
55291         }
55292         if(isUpdate !== true){
55293             this.stripeRows(index);
55294             this.syncRowHeights(index, index);
55295             this.layout();
55296             this.fireEvent("rowremoved", this, index, record);
55297         }
55298     },
55299
55300     onLoad : function(){
55301         this.scrollToTop();
55302     },
55303
55304     /**
55305      * Scrolls the grid to the top
55306      */
55307     scrollToTop : function(){
55308         if(this.scroller){
55309             this.scroller.dom.scrollTop = 0;
55310             this.syncScroll();
55311         }
55312     },
55313
55314     /**
55315      * Gets a panel in the header of the grid that can be used for toolbars etc.
55316      * After modifying the contents of this panel a call to grid.autoSize() may be
55317      * required to register any changes in size.
55318      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55319      * @return Roo.Element
55320      */
55321     getHeaderPanel : function(doShow){
55322         if(doShow){
55323             this.headerPanel.show();
55324         }
55325         return this.headerPanel;
55326     },
55327
55328     /**
55329      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55330      * After modifying the contents of this panel a call to grid.autoSize() may be
55331      * required to register any changes in size.
55332      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55333      * @return Roo.Element
55334      */
55335     getFooterPanel : function(doShow){
55336         if(doShow){
55337             this.footerPanel.show();
55338         }
55339         return this.footerPanel;
55340     },
55341
55342     initElements : function(){
55343         var E = Roo.Element;
55344         var el = this.grid.getGridEl().dom.firstChild;
55345         var cs = el.childNodes;
55346
55347         this.el = new E(el);
55348         
55349          this.focusEl = new E(el.firstChild);
55350         this.focusEl.swallowEvent("click", true);
55351         
55352         this.headerPanel = new E(cs[1]);
55353         this.headerPanel.enableDisplayMode("block");
55354
55355         this.scroller = new E(cs[2]);
55356         this.scrollSizer = new E(this.scroller.dom.firstChild);
55357
55358         this.lockedWrap = new E(cs[3]);
55359         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55360         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55361
55362         this.mainWrap = new E(cs[4]);
55363         this.mainHd = new E(this.mainWrap.dom.firstChild);
55364         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55365
55366         this.footerPanel = new E(cs[5]);
55367         this.footerPanel.enableDisplayMode("block");
55368
55369         this.resizeProxy = new E(cs[6]);
55370
55371         this.headerSelector = String.format(
55372            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55373            this.lockedHd.id, this.mainHd.id
55374         );
55375
55376         this.splitterSelector = String.format(
55377            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55378            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55379         );
55380     },
55381     idToCssName : function(s)
55382     {
55383         return s.replace(/[^a-z0-9]+/ig, '-');
55384     },
55385
55386     getHeaderCell : function(index){
55387         return Roo.DomQuery.select(this.headerSelector)[index];
55388     },
55389
55390     getHeaderCellMeasure : function(index){
55391         return this.getHeaderCell(index).firstChild;
55392     },
55393
55394     getHeaderCellText : function(index){
55395         return this.getHeaderCell(index).firstChild.firstChild;
55396     },
55397
55398     getLockedTable : function(){
55399         return this.lockedBody.dom.firstChild;
55400     },
55401
55402     getBodyTable : function(){
55403         return this.mainBody.dom.firstChild;
55404     },
55405
55406     getLockedRow : function(index){
55407         return this.getLockedTable().rows[index];
55408     },
55409
55410     getRow : function(index){
55411         return this.getBodyTable().rows[index];
55412     },
55413
55414     getRowComposite : function(index){
55415         if(!this.rowEl){
55416             this.rowEl = new Roo.CompositeElementLite();
55417         }
55418         var els = [], lrow, mrow;
55419         if(lrow = this.getLockedRow(index)){
55420             els.push(lrow);
55421         }
55422         if(mrow = this.getRow(index)){
55423             els.push(mrow);
55424         }
55425         this.rowEl.elements = els;
55426         return this.rowEl;
55427     },
55428     /**
55429      * Gets the 'td' of the cell
55430      * 
55431      * @param {Integer} rowIndex row to select
55432      * @param {Integer} colIndex column to select
55433      * 
55434      * @return {Object} 
55435      */
55436     getCell : function(rowIndex, colIndex){
55437         var locked = this.cm.getLockedCount();
55438         var source;
55439         if(colIndex < locked){
55440             source = this.lockedBody.dom.firstChild;
55441         }else{
55442             source = this.mainBody.dom.firstChild;
55443             colIndex -= locked;
55444         }
55445         return source.rows[rowIndex].childNodes[colIndex];
55446     },
55447
55448     getCellText : function(rowIndex, colIndex){
55449         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55450     },
55451
55452     getCellBox : function(cell){
55453         var b = this.fly(cell).getBox();
55454         if(Roo.isOpera){ // opera fails to report the Y
55455             b.y = cell.offsetTop + this.mainBody.getY();
55456         }
55457         return b;
55458     },
55459
55460     getCellIndex : function(cell){
55461         var id = String(cell.className).match(this.cellRE);
55462         if(id){
55463             return parseInt(id[1], 10);
55464         }
55465         return 0;
55466     },
55467
55468     findHeaderIndex : function(n){
55469         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55470         return r ? this.getCellIndex(r) : false;
55471     },
55472
55473     findHeaderCell : function(n){
55474         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55475         return r ? r : false;
55476     },
55477
55478     findRowIndex : function(n){
55479         if(!n){
55480             return false;
55481         }
55482         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55483         return r ? r.rowIndex : false;
55484     },
55485
55486     findCellIndex : function(node){
55487         var stop = this.el.dom;
55488         while(node && node != stop){
55489             if(this.findRE.test(node.className)){
55490                 return this.getCellIndex(node);
55491             }
55492             node = node.parentNode;
55493         }
55494         return false;
55495     },
55496
55497     getColumnId : function(index){
55498         return this.cm.getColumnId(index);
55499     },
55500
55501     getSplitters : function()
55502     {
55503         if(this.splitterSelector){
55504            return Roo.DomQuery.select(this.splitterSelector);
55505         }else{
55506             return null;
55507       }
55508     },
55509
55510     getSplitter : function(index){
55511         return this.getSplitters()[index];
55512     },
55513
55514     onRowOver : function(e, t){
55515         var row;
55516         if((row = this.findRowIndex(t)) !== false){
55517             this.getRowComposite(row).addClass("x-grid-row-over");
55518         }
55519     },
55520
55521     onRowOut : function(e, t){
55522         var row;
55523         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55524             this.getRowComposite(row).removeClass("x-grid-row-over");
55525         }
55526     },
55527
55528     renderHeaders : function(){
55529         var cm = this.cm;
55530         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55531         var cb = [], lb = [], sb = [], lsb = [], p = {};
55532         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55533             p.cellId = "x-grid-hd-0-" + i;
55534             p.splitId = "x-grid-csplit-0-" + i;
55535             p.id = cm.getColumnId(i);
55536             p.value = cm.getColumnHeader(i) || "";
55537             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55538             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55539             if(!cm.isLocked(i)){
55540                 cb[cb.length] = ct.apply(p);
55541                 sb[sb.length] = st.apply(p);
55542             }else{
55543                 lb[lb.length] = ct.apply(p);
55544                 lsb[lsb.length] = st.apply(p);
55545             }
55546         }
55547         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55548                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55549     },
55550
55551     updateHeaders : function(){
55552         var html = this.renderHeaders();
55553         this.lockedHd.update(html[0]);
55554         this.mainHd.update(html[1]);
55555     },
55556
55557     /**
55558      * Focuses the specified row.
55559      * @param {Number} row The row index
55560      */
55561     focusRow : function(row)
55562     {
55563         //Roo.log('GridView.focusRow');
55564         var x = this.scroller.dom.scrollLeft;
55565         this.focusCell(row, 0, false);
55566         this.scroller.dom.scrollLeft = x;
55567     },
55568
55569     /**
55570      * Focuses the specified cell.
55571      * @param {Number} row The row index
55572      * @param {Number} col The column index
55573      * @param {Boolean} hscroll false to disable horizontal scrolling
55574      */
55575     focusCell : function(row, col, hscroll)
55576     {
55577         //Roo.log('GridView.focusCell');
55578         var el = this.ensureVisible(row, col, hscroll);
55579         this.focusEl.alignTo(el, "tl-tl");
55580         if(Roo.isGecko){
55581             this.focusEl.focus();
55582         }else{
55583             this.focusEl.focus.defer(1, this.focusEl);
55584         }
55585     },
55586
55587     /**
55588      * Scrolls the specified cell into view
55589      * @param {Number} row The row index
55590      * @param {Number} col The column index
55591      * @param {Boolean} hscroll false to disable horizontal scrolling
55592      */
55593     ensureVisible : function(row, col, hscroll)
55594     {
55595         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55596         //return null; //disable for testing.
55597         if(typeof row != "number"){
55598             row = row.rowIndex;
55599         }
55600         if(row < 0 && row >= this.ds.getCount()){
55601             return  null;
55602         }
55603         col = (col !== undefined ? col : 0);
55604         var cm = this.grid.colModel;
55605         while(cm.isHidden(col)){
55606             col++;
55607         }
55608
55609         var el = this.getCell(row, col);
55610         if(!el){
55611             return null;
55612         }
55613         var c = this.scroller.dom;
55614
55615         var ctop = parseInt(el.offsetTop, 10);
55616         var cleft = parseInt(el.offsetLeft, 10);
55617         var cbot = ctop + el.offsetHeight;
55618         var cright = cleft + el.offsetWidth;
55619         
55620         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55621         var stop = parseInt(c.scrollTop, 10);
55622         var sleft = parseInt(c.scrollLeft, 10);
55623         var sbot = stop + ch;
55624         var sright = sleft + c.clientWidth;
55625         /*
55626         Roo.log('GridView.ensureVisible:' +
55627                 ' ctop:' + ctop +
55628                 ' c.clientHeight:' + c.clientHeight +
55629                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55630                 ' stop:' + stop +
55631                 ' cbot:' + cbot +
55632                 ' sbot:' + sbot +
55633                 ' ch:' + ch  
55634                 );
55635         */
55636         if(ctop < stop){
55637              c.scrollTop = ctop;
55638             //Roo.log("set scrolltop to ctop DISABLE?");
55639         }else if(cbot > sbot){
55640             //Roo.log("set scrolltop to cbot-ch");
55641             c.scrollTop = cbot-ch;
55642         }
55643         
55644         if(hscroll !== false){
55645             if(cleft < sleft){
55646                 c.scrollLeft = cleft;
55647             }else if(cright > sright){
55648                 c.scrollLeft = cright-c.clientWidth;
55649             }
55650         }
55651          
55652         return el;
55653     },
55654
55655     updateColumns : function(){
55656         this.grid.stopEditing();
55657         var cm = this.grid.colModel, colIds = this.getColumnIds();
55658         //var totalWidth = cm.getTotalWidth();
55659         var pos = 0;
55660         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55661             //if(cm.isHidden(i)) continue;
55662             var w = cm.getColumnWidth(i);
55663             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55664             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55665         }
55666         this.updateSplitters();
55667     },
55668
55669     generateRules : function(cm){
55670         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55671         Roo.util.CSS.removeStyleSheet(rulesId);
55672         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55673             var cid = cm.getColumnId(i);
55674             var align = '';
55675             if(cm.config[i].align){
55676                 align = 'text-align:'+cm.config[i].align+';';
55677             }
55678             var hidden = '';
55679             if(cm.isHidden(i)){
55680                 hidden = 'display:none;';
55681             }
55682             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55683             ruleBuf.push(
55684                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55685                     this.hdSelector, cid, " {\n", align, width, "}\n",
55686                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55687                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55688         }
55689         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55690     },
55691
55692     updateSplitters : function(){
55693         var cm = this.cm, s = this.getSplitters();
55694         if(s){ // splitters not created yet
55695             var pos = 0, locked = true;
55696             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55697                 if(cm.isHidden(i)) {
55698                     continue;
55699                 }
55700                 var w = cm.getColumnWidth(i); // make sure it's a number
55701                 if(!cm.isLocked(i) && locked){
55702                     pos = 0;
55703                     locked = false;
55704                 }
55705                 pos += w;
55706                 s[i].style.left = (pos-this.splitOffset) + "px";
55707             }
55708         }
55709     },
55710
55711     handleHiddenChange : function(colModel, colIndex, hidden){
55712         if(hidden){
55713             this.hideColumn(colIndex);
55714         }else{
55715             this.unhideColumn(colIndex);
55716         }
55717     },
55718
55719     hideColumn : function(colIndex){
55720         var cid = this.getColumnId(colIndex);
55721         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55722         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55723         if(Roo.isSafari){
55724             this.updateHeaders();
55725         }
55726         this.updateSplitters();
55727         this.layout();
55728     },
55729
55730     unhideColumn : function(colIndex){
55731         var cid = this.getColumnId(colIndex);
55732         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55733         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55734
55735         if(Roo.isSafari){
55736             this.updateHeaders();
55737         }
55738         this.updateSplitters();
55739         this.layout();
55740     },
55741
55742     insertRows : function(dm, firstRow, lastRow, isUpdate){
55743         if(firstRow == 0 && lastRow == dm.getCount()-1){
55744             this.refresh();
55745         }else{
55746             if(!isUpdate){
55747                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55748             }
55749             var s = this.getScrollState();
55750             var markup = this.renderRows(firstRow, lastRow);
55751             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55752             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55753             this.restoreScroll(s);
55754             if(!isUpdate){
55755                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55756                 this.syncRowHeights(firstRow, lastRow);
55757                 this.stripeRows(firstRow);
55758                 this.layout();
55759             }
55760         }
55761     },
55762
55763     bufferRows : function(markup, target, index){
55764         var before = null, trows = target.rows, tbody = target.tBodies[0];
55765         if(index < trows.length){
55766             before = trows[index];
55767         }
55768         var b = document.createElement("div");
55769         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55770         var rows = b.firstChild.rows;
55771         for(var i = 0, len = rows.length; i < len; i++){
55772             if(before){
55773                 tbody.insertBefore(rows[0], before);
55774             }else{
55775                 tbody.appendChild(rows[0]);
55776             }
55777         }
55778         b.innerHTML = "";
55779         b = null;
55780     },
55781
55782     deleteRows : function(dm, firstRow, lastRow){
55783         if(dm.getRowCount()<1){
55784             this.fireEvent("beforerefresh", this);
55785             this.mainBody.update("");
55786             this.lockedBody.update("");
55787             this.fireEvent("refresh", this);
55788         }else{
55789             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55790             var bt = this.getBodyTable();
55791             var tbody = bt.firstChild;
55792             var rows = bt.rows;
55793             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55794                 tbody.removeChild(rows[firstRow]);
55795             }
55796             this.stripeRows(firstRow);
55797             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55798         }
55799     },
55800
55801     updateRows : function(dataSource, firstRow, lastRow){
55802         var s = this.getScrollState();
55803         this.refresh();
55804         this.restoreScroll(s);
55805     },
55806
55807     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55808         if(!noRefresh){
55809            this.refresh();
55810         }
55811         this.updateHeaderSortState();
55812     },
55813
55814     getScrollState : function(){
55815         
55816         var sb = this.scroller.dom;
55817         return {left: sb.scrollLeft, top: sb.scrollTop};
55818     },
55819
55820     stripeRows : function(startRow){
55821         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55822             return;
55823         }
55824         startRow = startRow || 0;
55825         var rows = this.getBodyTable().rows;
55826         var lrows = this.getLockedTable().rows;
55827         var cls = ' x-grid-row-alt ';
55828         for(var i = startRow, len = rows.length; i < len; i++){
55829             var row = rows[i], lrow = lrows[i];
55830             var isAlt = ((i+1) % 2 == 0);
55831             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55832             if(isAlt == hasAlt){
55833                 continue;
55834             }
55835             if(isAlt){
55836                 row.className += " x-grid-row-alt";
55837             }else{
55838                 row.className = row.className.replace("x-grid-row-alt", "");
55839             }
55840             if(lrow){
55841                 lrow.className = row.className;
55842             }
55843         }
55844     },
55845
55846     restoreScroll : function(state){
55847         //Roo.log('GridView.restoreScroll');
55848         var sb = this.scroller.dom;
55849         sb.scrollLeft = state.left;
55850         sb.scrollTop = state.top;
55851         this.syncScroll();
55852     },
55853
55854     syncScroll : function(){
55855         //Roo.log('GridView.syncScroll');
55856         var sb = this.scroller.dom;
55857         var sh = this.mainHd.dom;
55858         var bs = this.mainBody.dom;
55859         var lv = this.lockedBody.dom;
55860         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55861         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55862     },
55863
55864     handleScroll : function(e){
55865         this.syncScroll();
55866         var sb = this.scroller.dom;
55867         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55868         e.stopEvent();
55869     },
55870
55871     handleWheel : function(e){
55872         var d = e.getWheelDelta();
55873         this.scroller.dom.scrollTop -= d*22;
55874         // set this here to prevent jumpy scrolling on large tables
55875         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55876         e.stopEvent();
55877     },
55878
55879     renderRows : function(startRow, endRow){
55880         // pull in all the crap needed to render rows
55881         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55882         var colCount = cm.getColumnCount();
55883
55884         if(ds.getCount() < 1){
55885             return ["", ""];
55886         }
55887
55888         // build a map for all the columns
55889         var cs = [];
55890         for(var i = 0; i < colCount; i++){
55891             var name = cm.getDataIndex(i);
55892             cs[i] = {
55893                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55894                 renderer : cm.getRenderer(i),
55895                 id : cm.getColumnId(i),
55896                 locked : cm.isLocked(i),
55897                 has_editor : cm.isCellEditable(i)
55898             };
55899         }
55900
55901         startRow = startRow || 0;
55902         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55903
55904         // records to render
55905         var rs = ds.getRange(startRow, endRow);
55906
55907         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55908     },
55909
55910     // As much as I hate to duplicate code, this was branched because FireFox really hates
55911     // [].join("") on strings. The performance difference was substantial enough to
55912     // branch this function
55913     doRender : Roo.isGecko ?
55914             function(cs, rs, ds, startRow, colCount, stripe){
55915                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55916                 // buffers
55917                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55918                 
55919                 var hasListener = this.grid.hasListener('rowclass');
55920                 var rowcfg = {};
55921                 for(var j = 0, len = rs.length; j < len; j++){
55922                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55923                     for(var i = 0; i < colCount; i++){
55924                         c = cs[i];
55925                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55926                         p.id = c.id;
55927                         p.css = p.attr = "";
55928                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55929                         if(p.value == undefined || p.value === "") {
55930                             p.value = "&#160;";
55931                         }
55932                         if(c.has_editor){
55933                             p.css += ' x-grid-editable-cell';
55934                         }
55935                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55936                             p.css +=  ' x-grid-dirty-cell';
55937                         }
55938                         var markup = ct.apply(p);
55939                         if(!c.locked){
55940                             cb+= markup;
55941                         }else{
55942                             lcb+= markup;
55943                         }
55944                     }
55945                     var alt = [];
55946                     if(stripe && ((rowIndex+1) % 2 == 0)){
55947                         alt.push("x-grid-row-alt")
55948                     }
55949                     if(r.dirty){
55950                         alt.push(  " x-grid-dirty-row");
55951                     }
55952                     rp.cells = lcb;
55953                     if(this.getRowClass){
55954                         alt.push(this.getRowClass(r, rowIndex));
55955                     }
55956                     if (hasListener) {
55957                         rowcfg = {
55958                              
55959                             record: r,
55960                             rowIndex : rowIndex,
55961                             rowClass : ''
55962                         };
55963                         this.grid.fireEvent('rowclass', this, rowcfg);
55964                         alt.push(rowcfg.rowClass);
55965                     }
55966                     rp.alt = alt.join(" ");
55967                     lbuf+= rt.apply(rp);
55968                     rp.cells = cb;
55969                     buf+=  rt.apply(rp);
55970                 }
55971                 return [lbuf, buf];
55972             } :
55973             function(cs, rs, ds, startRow, colCount, stripe){
55974                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55975                 // buffers
55976                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55977                 var hasListener = this.grid.hasListener('rowclass');
55978  
55979                 var rowcfg = {};
55980                 for(var j = 0, len = rs.length; j < len; j++){
55981                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55982                     for(var i = 0; i < colCount; i++){
55983                         c = cs[i];
55984                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55985                         p.id = c.id;
55986                         p.css = p.attr = "";
55987                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55988                         if(p.value == undefined || p.value === "") {
55989                             p.value = "&#160;";
55990                         }
55991                         //Roo.log(c);
55992                          if(c.has_editor){
55993                             p.css += ' x-grid-editable-cell';
55994                         }
55995                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55996                             p.css += ' x-grid-dirty-cell' 
55997                         }
55998                         
55999                         var markup = ct.apply(p);
56000                         if(!c.locked){
56001                             cb[cb.length] = markup;
56002                         }else{
56003                             lcb[lcb.length] = markup;
56004                         }
56005                     }
56006                     var alt = [];
56007                     if(stripe && ((rowIndex+1) % 2 == 0)){
56008                         alt.push( "x-grid-row-alt");
56009                     }
56010                     if(r.dirty){
56011                         alt.push(" x-grid-dirty-row");
56012                     }
56013                     rp.cells = lcb;
56014                     if(this.getRowClass){
56015                         alt.push( this.getRowClass(r, rowIndex));
56016                     }
56017                     if (hasListener) {
56018                         rowcfg = {
56019                              
56020                             record: r,
56021                             rowIndex : rowIndex,
56022                             rowClass : ''
56023                         };
56024                         this.grid.fireEvent('rowclass', this, rowcfg);
56025                         alt.push(rowcfg.rowClass);
56026                     }
56027                     
56028                     rp.alt = alt.join(" ");
56029                     rp.cells = lcb.join("");
56030                     lbuf[lbuf.length] = rt.apply(rp);
56031                     rp.cells = cb.join("");
56032                     buf[buf.length] =  rt.apply(rp);
56033                 }
56034                 return [lbuf.join(""), buf.join("")];
56035             },
56036
56037     renderBody : function(){
56038         var markup = this.renderRows();
56039         var bt = this.templates.body;
56040         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56041     },
56042
56043     /**
56044      * Refreshes the grid
56045      * @param {Boolean} headersToo
56046      */
56047     refresh : function(headersToo){
56048         this.fireEvent("beforerefresh", this);
56049         this.grid.stopEditing();
56050         var result = this.renderBody();
56051         this.lockedBody.update(result[0]);
56052         this.mainBody.update(result[1]);
56053         if(headersToo === true){
56054             this.updateHeaders();
56055             this.updateColumns();
56056             this.updateSplitters();
56057             this.updateHeaderSortState();
56058         }
56059         this.syncRowHeights();
56060         this.layout();
56061         this.fireEvent("refresh", this);
56062     },
56063
56064     handleColumnMove : function(cm, oldIndex, newIndex){
56065         this.indexMap = null;
56066         var s = this.getScrollState();
56067         this.refresh(true);
56068         this.restoreScroll(s);
56069         this.afterMove(newIndex);
56070     },
56071
56072     afterMove : function(colIndex){
56073         if(this.enableMoveAnim && Roo.enableFx){
56074             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56075         }
56076         // if multisort - fix sortOrder, and reload..
56077         if (this.grid.dataSource.multiSort) {
56078             // the we can call sort again..
56079             var dm = this.grid.dataSource;
56080             var cm = this.grid.colModel;
56081             var so = [];
56082             for(var i = 0; i < cm.config.length; i++ ) {
56083                 
56084                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56085                     continue; // dont' bother, it's not in sort list or being set.
56086                 }
56087                 
56088                 so.push(cm.config[i].dataIndex);
56089             };
56090             dm.sortOrder = so;
56091             dm.load(dm.lastOptions);
56092             
56093             
56094         }
56095         
56096     },
56097
56098     updateCell : function(dm, rowIndex, dataIndex){
56099         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56100         if(typeof colIndex == "undefined"){ // not present in grid
56101             return;
56102         }
56103         var cm = this.grid.colModel;
56104         var cell = this.getCell(rowIndex, colIndex);
56105         var cellText = this.getCellText(rowIndex, colIndex);
56106
56107         var p = {
56108             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56109             id : cm.getColumnId(colIndex),
56110             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56111         };
56112         var renderer = cm.getRenderer(colIndex);
56113         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56114         if(typeof val == "undefined" || val === "") {
56115             val = "&#160;";
56116         }
56117         cellText.innerHTML = val;
56118         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56119         this.syncRowHeights(rowIndex, rowIndex);
56120     },
56121
56122     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56123         var maxWidth = 0;
56124         if(this.grid.autoSizeHeaders){
56125             var h = this.getHeaderCellMeasure(colIndex);
56126             maxWidth = Math.max(maxWidth, h.scrollWidth);
56127         }
56128         var tb, index;
56129         if(this.cm.isLocked(colIndex)){
56130             tb = this.getLockedTable();
56131             index = colIndex;
56132         }else{
56133             tb = this.getBodyTable();
56134             index = colIndex - this.cm.getLockedCount();
56135         }
56136         if(tb && tb.rows){
56137             var rows = tb.rows;
56138             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56139             for(var i = 0; i < stopIndex; i++){
56140                 var cell = rows[i].childNodes[index].firstChild;
56141                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56142             }
56143         }
56144         return maxWidth + /*margin for error in IE*/ 5;
56145     },
56146     /**
56147      * Autofit a column to its content.
56148      * @param {Number} colIndex
56149      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56150      */
56151      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56152          if(this.cm.isHidden(colIndex)){
56153              return; // can't calc a hidden column
56154          }
56155         if(forceMinSize){
56156             var cid = this.cm.getColumnId(colIndex);
56157             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56158            if(this.grid.autoSizeHeaders){
56159                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56160            }
56161         }
56162         var newWidth = this.calcColumnWidth(colIndex);
56163         this.cm.setColumnWidth(colIndex,
56164             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56165         if(!suppressEvent){
56166             this.grid.fireEvent("columnresize", colIndex, newWidth);
56167         }
56168     },
56169
56170     /**
56171      * Autofits all columns to their content and then expands to fit any extra space in the grid
56172      */
56173      autoSizeColumns : function(){
56174         var cm = this.grid.colModel;
56175         var colCount = cm.getColumnCount();
56176         for(var i = 0; i < colCount; i++){
56177             this.autoSizeColumn(i, true, true);
56178         }
56179         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56180             this.fitColumns();
56181         }else{
56182             this.updateColumns();
56183             this.layout();
56184         }
56185     },
56186
56187     /**
56188      * Autofits all columns to the grid's width proportionate with their current size
56189      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56190      */
56191     fitColumns : function(reserveScrollSpace){
56192         var cm = this.grid.colModel;
56193         var colCount = cm.getColumnCount();
56194         var cols = [];
56195         var width = 0;
56196         var i, w;
56197         for (i = 0; i < colCount; i++){
56198             if(!cm.isHidden(i) && !cm.isFixed(i)){
56199                 w = cm.getColumnWidth(i);
56200                 cols.push(i);
56201                 cols.push(w);
56202                 width += w;
56203             }
56204         }
56205         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56206         if(reserveScrollSpace){
56207             avail -= 17;
56208         }
56209         var frac = (avail - cm.getTotalWidth())/width;
56210         while (cols.length){
56211             w = cols.pop();
56212             i = cols.pop();
56213             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56214         }
56215         this.updateColumns();
56216         this.layout();
56217     },
56218
56219     onRowSelect : function(rowIndex){
56220         var row = this.getRowComposite(rowIndex);
56221         row.addClass("x-grid-row-selected");
56222     },
56223
56224     onRowDeselect : function(rowIndex){
56225         var row = this.getRowComposite(rowIndex);
56226         row.removeClass("x-grid-row-selected");
56227     },
56228
56229     onCellSelect : function(row, col){
56230         var cell = this.getCell(row, col);
56231         if(cell){
56232             Roo.fly(cell).addClass("x-grid-cell-selected");
56233         }
56234     },
56235
56236     onCellDeselect : function(row, col){
56237         var cell = this.getCell(row, col);
56238         if(cell){
56239             Roo.fly(cell).removeClass("x-grid-cell-selected");
56240         }
56241     },
56242
56243     updateHeaderSortState : function(){
56244         
56245         // sort state can be single { field: xxx, direction : yyy}
56246         // or   { xxx=>ASC , yyy : DESC ..... }
56247         
56248         var mstate = {};
56249         if (!this.ds.multiSort) { 
56250             var state = this.ds.getSortState();
56251             if(!state){
56252                 return;
56253             }
56254             mstate[state.field] = state.direction;
56255             // FIXME... - this is not used here.. but might be elsewhere..
56256             this.sortState = state;
56257             
56258         } else {
56259             mstate = this.ds.sortToggle;
56260         }
56261         //remove existing sort classes..
56262         
56263         var sc = this.sortClasses;
56264         var hds = this.el.select(this.headerSelector).removeClass(sc);
56265         
56266         for(var f in mstate) {
56267         
56268             var sortColumn = this.cm.findColumnIndex(f);
56269             
56270             if(sortColumn != -1){
56271                 var sortDir = mstate[f];        
56272                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56273             }
56274         }
56275         
56276          
56277         
56278     },
56279
56280
56281     handleHeaderClick : function(g, index,e){
56282         
56283         Roo.log("header click");
56284         
56285         if (Roo.isTouch) {
56286             // touch events on header are handled by context
56287             this.handleHdCtx(g,index,e);
56288             return;
56289         }
56290         
56291         
56292         if(this.headersDisabled){
56293             return;
56294         }
56295         var dm = g.dataSource, cm = g.colModel;
56296         if(!cm.isSortable(index)){
56297             return;
56298         }
56299         g.stopEditing();
56300         
56301         if (dm.multiSort) {
56302             // update the sortOrder
56303             var so = [];
56304             for(var i = 0; i < cm.config.length; i++ ) {
56305                 
56306                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56307                     continue; // dont' bother, it's not in sort list or being set.
56308                 }
56309                 
56310                 so.push(cm.config[i].dataIndex);
56311             };
56312             dm.sortOrder = so;
56313         }
56314         
56315         
56316         dm.sort(cm.getDataIndex(index));
56317     },
56318
56319
56320     destroy : function(){
56321         if(this.colMenu){
56322             this.colMenu.removeAll();
56323             Roo.menu.MenuMgr.unregister(this.colMenu);
56324             this.colMenu.getEl().remove();
56325             delete this.colMenu;
56326         }
56327         if(this.hmenu){
56328             this.hmenu.removeAll();
56329             Roo.menu.MenuMgr.unregister(this.hmenu);
56330             this.hmenu.getEl().remove();
56331             delete this.hmenu;
56332         }
56333         if(this.grid.enableColumnMove){
56334             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56335             if(dds){
56336                 for(var dd in dds){
56337                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56338                         var elid = dds[dd].dragElId;
56339                         dds[dd].unreg();
56340                         Roo.get(elid).remove();
56341                     } else if(dds[dd].config.isTarget){
56342                         dds[dd].proxyTop.remove();
56343                         dds[dd].proxyBottom.remove();
56344                         dds[dd].unreg();
56345                     }
56346                     if(Roo.dd.DDM.locationCache[dd]){
56347                         delete Roo.dd.DDM.locationCache[dd];
56348                     }
56349                 }
56350                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56351             }
56352         }
56353         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56354         this.bind(null, null);
56355         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56356     },
56357
56358     handleLockChange : function(){
56359         this.refresh(true);
56360     },
56361
56362     onDenyColumnLock : function(){
56363
56364     },
56365
56366     onDenyColumnHide : function(){
56367
56368     },
56369
56370     handleHdMenuClick : function(item){
56371         var index = this.hdCtxIndex;
56372         var cm = this.cm, ds = this.ds;
56373         switch(item.id){
56374             case "asc":
56375                 ds.sort(cm.getDataIndex(index), "ASC");
56376                 break;
56377             case "desc":
56378                 ds.sort(cm.getDataIndex(index), "DESC");
56379                 break;
56380             case "lock":
56381                 var lc = cm.getLockedCount();
56382                 if(cm.getColumnCount(true) <= lc+1){
56383                     this.onDenyColumnLock();
56384                     return;
56385                 }
56386                 if(lc != index){
56387                     cm.setLocked(index, true, true);
56388                     cm.moveColumn(index, lc);
56389                     this.grid.fireEvent("columnmove", index, lc);
56390                 }else{
56391                     cm.setLocked(index, true);
56392                 }
56393             break;
56394             case "unlock":
56395                 var lc = cm.getLockedCount();
56396                 if((lc-1) != index){
56397                     cm.setLocked(index, false, true);
56398                     cm.moveColumn(index, lc-1);
56399                     this.grid.fireEvent("columnmove", index, lc-1);
56400                 }else{
56401                     cm.setLocked(index, false);
56402                 }
56403             break;
56404             case 'wider': // used to expand cols on touch..
56405             case 'narrow':
56406                 var cw = cm.getColumnWidth(index);
56407                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56408                 cw = Math.max(0, cw);
56409                 cw = Math.min(cw,4000);
56410                 cm.setColumnWidth(index, cw);
56411                 break;
56412                 
56413             default:
56414                 index = cm.getIndexById(item.id.substr(4));
56415                 if(index != -1){
56416                     if(item.checked && cm.getColumnCount(true) <= 1){
56417                         this.onDenyColumnHide();
56418                         return false;
56419                     }
56420                     cm.setHidden(index, item.checked);
56421                 }
56422         }
56423         return true;
56424     },
56425
56426     beforeColMenuShow : function(){
56427         var cm = this.cm,  colCount = cm.getColumnCount();
56428         this.colMenu.removeAll();
56429         for(var i = 0; i < colCount; i++){
56430             this.colMenu.add(new Roo.menu.CheckItem({
56431                 id: "col-"+cm.getColumnId(i),
56432                 text: cm.getColumnHeader(i),
56433                 checked: !cm.isHidden(i),
56434                 hideOnClick:false
56435             }));
56436         }
56437     },
56438
56439     handleHdCtx : function(g, index, e){
56440         e.stopEvent();
56441         var hd = this.getHeaderCell(index);
56442         this.hdCtxIndex = index;
56443         var ms = this.hmenu.items, cm = this.cm;
56444         ms.get("asc").setDisabled(!cm.isSortable(index));
56445         ms.get("desc").setDisabled(!cm.isSortable(index));
56446         if(this.grid.enableColLock !== false){
56447             ms.get("lock").setDisabled(cm.isLocked(index));
56448             ms.get("unlock").setDisabled(!cm.isLocked(index));
56449         }
56450         this.hmenu.show(hd, "tl-bl");
56451     },
56452
56453     handleHdOver : function(e){
56454         var hd = this.findHeaderCell(e.getTarget());
56455         if(hd && !this.headersDisabled){
56456             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56457                this.fly(hd).addClass("x-grid-hd-over");
56458             }
56459         }
56460     },
56461
56462     handleHdOut : function(e){
56463         var hd = this.findHeaderCell(e.getTarget());
56464         if(hd){
56465             this.fly(hd).removeClass("x-grid-hd-over");
56466         }
56467     },
56468
56469     handleSplitDblClick : function(e, t){
56470         var i = this.getCellIndex(t);
56471         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56472             this.autoSizeColumn(i, true);
56473             this.layout();
56474         }
56475     },
56476
56477     render : function(){
56478
56479         var cm = this.cm;
56480         var colCount = cm.getColumnCount();
56481
56482         if(this.grid.monitorWindowResize === true){
56483             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56484         }
56485         var header = this.renderHeaders();
56486         var body = this.templates.body.apply({rows:""});
56487         var html = this.templates.master.apply({
56488             lockedBody: body,
56489             body: body,
56490             lockedHeader: header[0],
56491             header: header[1]
56492         });
56493
56494         //this.updateColumns();
56495
56496         this.grid.getGridEl().dom.innerHTML = html;
56497
56498         this.initElements();
56499         
56500         // a kludge to fix the random scolling effect in webkit
56501         this.el.on("scroll", function() {
56502             this.el.dom.scrollTop=0; // hopefully not recursive..
56503         },this);
56504
56505         this.scroller.on("scroll", this.handleScroll, this);
56506         this.lockedBody.on("mousewheel", this.handleWheel, this);
56507         this.mainBody.on("mousewheel", this.handleWheel, this);
56508
56509         this.mainHd.on("mouseover", this.handleHdOver, this);
56510         this.mainHd.on("mouseout", this.handleHdOut, this);
56511         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56512                 {delegate: "."+this.splitClass});
56513
56514         this.lockedHd.on("mouseover", this.handleHdOver, this);
56515         this.lockedHd.on("mouseout", this.handleHdOut, this);
56516         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56517                 {delegate: "."+this.splitClass});
56518
56519         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56520             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56521         }
56522
56523         this.updateSplitters();
56524
56525         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56526             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56527             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56528         }
56529
56530         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56531             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56532             this.hmenu.add(
56533                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56534                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56535             );
56536             if(this.grid.enableColLock !== false){
56537                 this.hmenu.add('-',
56538                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56539                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56540                 );
56541             }
56542             if (Roo.isTouch) {
56543                  this.hmenu.add('-',
56544                     {id:"wider", text: this.columnsWiderText},
56545                     {id:"narrow", text: this.columnsNarrowText }
56546                 );
56547                 
56548                  
56549             }
56550             
56551             if(this.grid.enableColumnHide !== false){
56552
56553                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56554                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56555                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56556
56557                 this.hmenu.add('-',
56558                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56559                 );
56560             }
56561             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56562
56563             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56564         }
56565
56566         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56567             this.dd = new Roo.grid.GridDragZone(this.grid, {
56568                 ddGroup : this.grid.ddGroup || 'GridDD'
56569             });
56570             
56571         }
56572
56573         /*
56574         for(var i = 0; i < colCount; i++){
56575             if(cm.isHidden(i)){
56576                 this.hideColumn(i);
56577             }
56578             if(cm.config[i].align){
56579                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56580                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56581             }
56582         }*/
56583         
56584         this.updateHeaderSortState();
56585
56586         this.beforeInitialResize();
56587         this.layout(true);
56588
56589         // two part rendering gives faster view to the user
56590         this.renderPhase2.defer(1, this);
56591     },
56592
56593     renderPhase2 : function(){
56594         // render the rows now
56595         this.refresh();
56596         if(this.grid.autoSizeColumns){
56597             this.autoSizeColumns();
56598         }
56599     },
56600
56601     beforeInitialResize : function(){
56602
56603     },
56604
56605     onColumnSplitterMoved : function(i, w){
56606         this.userResized = true;
56607         var cm = this.grid.colModel;
56608         cm.setColumnWidth(i, w, true);
56609         var cid = cm.getColumnId(i);
56610         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56611         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56612         this.updateSplitters();
56613         this.layout();
56614         this.grid.fireEvent("columnresize", i, w);
56615     },
56616
56617     syncRowHeights : function(startIndex, endIndex){
56618         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56619             startIndex = startIndex || 0;
56620             var mrows = this.getBodyTable().rows;
56621             var lrows = this.getLockedTable().rows;
56622             var len = mrows.length-1;
56623             endIndex = Math.min(endIndex || len, len);
56624             for(var i = startIndex; i <= endIndex; i++){
56625                 var m = mrows[i], l = lrows[i];
56626                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56627                 m.style.height = l.style.height = h + "px";
56628             }
56629         }
56630     },
56631
56632     layout : function(initialRender, is2ndPass){
56633         var g = this.grid;
56634         var auto = g.autoHeight;
56635         var scrollOffset = 16;
56636         var c = g.getGridEl(), cm = this.cm,
56637                 expandCol = g.autoExpandColumn,
56638                 gv = this;
56639         //c.beginMeasure();
56640
56641         if(!c.dom.offsetWidth){ // display:none?
56642             if(initialRender){
56643                 this.lockedWrap.show();
56644                 this.mainWrap.show();
56645             }
56646             return;
56647         }
56648
56649         var hasLock = this.cm.isLocked(0);
56650
56651         var tbh = this.headerPanel.getHeight();
56652         var bbh = this.footerPanel.getHeight();
56653
56654         if(auto){
56655             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56656             var newHeight = ch + c.getBorderWidth("tb");
56657             if(g.maxHeight){
56658                 newHeight = Math.min(g.maxHeight, newHeight);
56659             }
56660             c.setHeight(newHeight);
56661         }
56662
56663         if(g.autoWidth){
56664             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56665         }
56666
56667         var s = this.scroller;
56668
56669         var csize = c.getSize(true);
56670
56671         this.el.setSize(csize.width, csize.height);
56672
56673         this.headerPanel.setWidth(csize.width);
56674         this.footerPanel.setWidth(csize.width);
56675
56676         var hdHeight = this.mainHd.getHeight();
56677         var vw = csize.width;
56678         var vh = csize.height - (tbh + bbh);
56679
56680         s.setSize(vw, vh);
56681
56682         var bt = this.getBodyTable();
56683         
56684         if(cm.getLockedCount() == cm.config.length){
56685             bt = this.getLockedTable();
56686         }
56687         
56688         var ltWidth = hasLock ?
56689                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56690
56691         var scrollHeight = bt.offsetHeight;
56692         var scrollWidth = ltWidth + bt.offsetWidth;
56693         var vscroll = false, hscroll = false;
56694
56695         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56696
56697         var lw = this.lockedWrap, mw = this.mainWrap;
56698         var lb = this.lockedBody, mb = this.mainBody;
56699
56700         setTimeout(function(){
56701             var t = s.dom.offsetTop;
56702             var w = s.dom.clientWidth,
56703                 h = s.dom.clientHeight;
56704
56705             lw.setTop(t);
56706             lw.setSize(ltWidth, h);
56707
56708             mw.setLeftTop(ltWidth, t);
56709             mw.setSize(w-ltWidth, h);
56710
56711             lb.setHeight(h-hdHeight);
56712             mb.setHeight(h-hdHeight);
56713
56714             if(is2ndPass !== true && !gv.userResized && expandCol){
56715                 // high speed resize without full column calculation
56716                 
56717                 var ci = cm.getIndexById(expandCol);
56718                 if (ci < 0) {
56719                     ci = cm.findColumnIndex(expandCol);
56720                 }
56721                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56722                 var expandId = cm.getColumnId(ci);
56723                 var  tw = cm.getTotalWidth(false);
56724                 var currentWidth = cm.getColumnWidth(ci);
56725                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56726                 if(currentWidth != cw){
56727                     cm.setColumnWidth(ci, cw, true);
56728                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56729                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56730                     gv.updateSplitters();
56731                     gv.layout(false, true);
56732                 }
56733             }
56734
56735             if(initialRender){
56736                 lw.show();
56737                 mw.show();
56738             }
56739             //c.endMeasure();
56740         }, 10);
56741     },
56742
56743     onWindowResize : function(){
56744         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56745             return;
56746         }
56747         this.layout();
56748     },
56749
56750     appendFooter : function(parentEl){
56751         return null;
56752     },
56753
56754     sortAscText : "Sort Ascending",
56755     sortDescText : "Sort Descending",
56756     lockText : "Lock Column",
56757     unlockText : "Unlock Column",
56758     columnsText : "Columns",
56759  
56760     columnsWiderText : "Wider",
56761     columnsNarrowText : "Thinner"
56762 });
56763
56764
56765 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56766     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56767     this.proxy.el.addClass('x-grid3-col-dd');
56768 };
56769
56770 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56771     handleMouseDown : function(e){
56772
56773     },
56774
56775     callHandleMouseDown : function(e){
56776         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56777     }
56778 });
56779 /*
56780  * Based on:
56781  * Ext JS Library 1.1.1
56782  * Copyright(c) 2006-2007, Ext JS, LLC.
56783  *
56784  * Originally Released Under LGPL - original licence link has changed is not relivant.
56785  *
56786  * Fork - LGPL
56787  * <script type="text/javascript">
56788  */
56789  
56790 // private
56791 // This is a support class used internally by the Grid components
56792 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56793     this.grid = grid;
56794     this.view = grid.getView();
56795     this.proxy = this.view.resizeProxy;
56796     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56797         "gridSplitters" + this.grid.getGridEl().id, {
56798         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56799     });
56800     this.setHandleElId(Roo.id(hd));
56801     this.setOuterHandleElId(Roo.id(hd2));
56802     this.scroll = false;
56803 };
56804 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56805     fly: Roo.Element.fly,
56806
56807     b4StartDrag : function(x, y){
56808         this.view.headersDisabled = true;
56809         this.proxy.setHeight(this.view.mainWrap.getHeight());
56810         var w = this.cm.getColumnWidth(this.cellIndex);
56811         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56812         this.resetConstraints();
56813         this.setXConstraint(minw, 1000);
56814         this.setYConstraint(0, 0);
56815         this.minX = x - minw;
56816         this.maxX = x + 1000;
56817         this.startPos = x;
56818         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56819     },
56820
56821
56822     handleMouseDown : function(e){
56823         ev = Roo.EventObject.setEvent(e);
56824         var t = this.fly(ev.getTarget());
56825         if(t.hasClass("x-grid-split")){
56826             this.cellIndex = this.view.getCellIndex(t.dom);
56827             this.split = t.dom;
56828             this.cm = this.grid.colModel;
56829             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56830                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56831             }
56832         }
56833     },
56834
56835     endDrag : function(e){
56836         this.view.headersDisabled = false;
56837         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56838         var diff = endX - this.startPos;
56839         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56840     },
56841
56842     autoOffset : function(){
56843         this.setDelta(0,0);
56844     }
56845 });/*
56846  * Based on:
56847  * Ext JS Library 1.1.1
56848  * Copyright(c) 2006-2007, Ext JS, LLC.
56849  *
56850  * Originally Released Under LGPL - original licence link has changed is not relivant.
56851  *
56852  * Fork - LGPL
56853  * <script type="text/javascript">
56854  */
56855  
56856 // private
56857 // This is a support class used internally by the Grid components
56858 Roo.grid.GridDragZone = function(grid, config){
56859     this.view = grid.getView();
56860     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56861     if(this.view.lockedBody){
56862         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56863         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56864     }
56865     this.scroll = false;
56866     this.grid = grid;
56867     this.ddel = document.createElement('div');
56868     this.ddel.className = 'x-grid-dd-wrap';
56869 };
56870
56871 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56872     ddGroup : "GridDD",
56873
56874     getDragData : function(e){
56875         var t = Roo.lib.Event.getTarget(e);
56876         var rowIndex = this.view.findRowIndex(t);
56877         var sm = this.grid.selModel;
56878             
56879         //Roo.log(rowIndex);
56880         
56881         if (sm.getSelectedCell) {
56882             // cell selection..
56883             if (!sm.getSelectedCell()) {
56884                 return false;
56885             }
56886             if (rowIndex != sm.getSelectedCell()[0]) {
56887                 return false;
56888             }
56889         
56890         }
56891         
56892         if(rowIndex !== false){
56893             
56894             // if editorgrid.. 
56895             
56896             
56897             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56898                
56899             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56900               //  
56901             //}
56902             if (e.hasModifier()){
56903                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56904             }
56905             
56906             Roo.log("getDragData");
56907             
56908             return {
56909                 grid: this.grid,
56910                 ddel: this.ddel,
56911                 rowIndex: rowIndex,
56912                 selections:sm.getSelections ? sm.getSelections() : (
56913                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56914                 )
56915             };
56916         }
56917         return false;
56918     },
56919
56920     onInitDrag : function(e){
56921         var data = this.dragData;
56922         this.ddel.innerHTML = this.grid.getDragDropText();
56923         this.proxy.update(this.ddel);
56924         // fire start drag?
56925     },
56926
56927     afterRepair : function(){
56928         this.dragging = false;
56929     },
56930
56931     getRepairXY : function(e, data){
56932         return false;
56933     },
56934
56935     onEndDrag : function(data, e){
56936         // fire end drag?
56937     },
56938
56939     onValidDrop : function(dd, e, id){
56940         // fire drag drop?
56941         this.hideProxy();
56942     },
56943
56944     beforeInvalidDrop : function(e, id){
56945
56946     }
56947 });/*
56948  * Based on:
56949  * Ext JS Library 1.1.1
56950  * Copyright(c) 2006-2007, Ext JS, LLC.
56951  *
56952  * Originally Released Under LGPL - original licence link has changed is not relivant.
56953  *
56954  * Fork - LGPL
56955  * <script type="text/javascript">
56956  */
56957  
56958
56959 /**
56960  * @class Roo.grid.ColumnModel
56961  * @extends Roo.util.Observable
56962  * This is the default implementation of a ColumnModel used by the Grid. It defines
56963  * the columns in the grid.
56964  * <br>Usage:<br>
56965  <pre><code>
56966  var colModel = new Roo.grid.ColumnModel([
56967         {header: "Ticker", width: 60, sortable: true, locked: true},
56968         {header: "Company Name", width: 150, sortable: true},
56969         {header: "Market Cap.", width: 100, sortable: true},
56970         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56971         {header: "Employees", width: 100, sortable: true, resizable: false}
56972  ]);
56973  </code></pre>
56974  * <p>
56975  
56976  * The config options listed for this class are options which may appear in each
56977  * individual column definition.
56978  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56979  * @constructor
56980  * @param {Object} config An Array of column config objects. See this class's
56981  * config objects for details.
56982 */
56983 Roo.grid.ColumnModel = function(config){
56984         /**
56985      * The config passed into the constructor
56986      */
56987     this.config = config;
56988     this.lookup = {};
56989
56990     // if no id, create one
56991     // if the column does not have a dataIndex mapping,
56992     // map it to the order it is in the config
56993     for(var i = 0, len = config.length; i < len; i++){
56994         var c = config[i];
56995         if(typeof c.dataIndex == "undefined"){
56996             c.dataIndex = i;
56997         }
56998         if(typeof c.renderer == "string"){
56999             c.renderer = Roo.util.Format[c.renderer];
57000         }
57001         if(typeof c.id == "undefined"){
57002             c.id = Roo.id();
57003         }
57004         if(c.editor && c.editor.xtype){
57005             c.editor  = Roo.factory(c.editor, Roo.grid);
57006         }
57007         if(c.editor && c.editor.isFormField){
57008             c.editor = new Roo.grid.GridEditor(c.editor);
57009         }
57010         this.lookup[c.id] = c;
57011     }
57012
57013     /**
57014      * The width of columns which have no width specified (defaults to 100)
57015      * @type Number
57016      */
57017     this.defaultWidth = 100;
57018
57019     /**
57020      * Default sortable of columns which have no sortable specified (defaults to false)
57021      * @type Boolean
57022      */
57023     this.defaultSortable = false;
57024
57025     this.addEvents({
57026         /**
57027              * @event widthchange
57028              * Fires when the width of a column changes.
57029              * @param {ColumnModel} this
57030              * @param {Number} columnIndex The column index
57031              * @param {Number} newWidth The new width
57032              */
57033             "widthchange": true,
57034         /**
57035              * @event headerchange
57036              * Fires when the text of a header changes.
57037              * @param {ColumnModel} this
57038              * @param {Number} columnIndex The column index
57039              * @param {Number} newText The new header text
57040              */
57041             "headerchange": true,
57042         /**
57043              * @event hiddenchange
57044              * Fires when a column is hidden or "unhidden".
57045              * @param {ColumnModel} this
57046              * @param {Number} columnIndex The column index
57047              * @param {Boolean} hidden true if hidden, false otherwise
57048              */
57049             "hiddenchange": true,
57050             /**
57051          * @event columnmoved
57052          * Fires when a column is moved.
57053          * @param {ColumnModel} this
57054          * @param {Number} oldIndex
57055          * @param {Number} newIndex
57056          */
57057         "columnmoved" : true,
57058         /**
57059          * @event columlockchange
57060          * Fires when a column's locked state is changed
57061          * @param {ColumnModel} this
57062          * @param {Number} colIndex
57063          * @param {Boolean} locked true if locked
57064          */
57065         "columnlockchange" : true
57066     });
57067     Roo.grid.ColumnModel.superclass.constructor.call(this);
57068 };
57069 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57070     /**
57071      * @cfg {String} header The header text to display in the Grid view.
57072      */
57073     /**
57074      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57075      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57076      * specified, the column's index is used as an index into the Record's data Array.
57077      */
57078     /**
57079      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57080      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57081      */
57082     /**
57083      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57084      * Defaults to the value of the {@link #defaultSortable} property.
57085      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57086      */
57087     /**
57088      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57089      */
57090     /**
57091      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57092      */
57093     /**
57094      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57095      */
57096     /**
57097      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57098      */
57099     /**
57100      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57101      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57102      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57103      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57104      */
57105        /**
57106      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57107      */
57108     /**
57109      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57110      */
57111     /**
57112      * @cfg {String} cursor (Optional)
57113      */
57114     /**
57115      * @cfg {String} tooltip (Optional)
57116      */
57117     /**
57118      * @cfg {Number} xs (Optional)
57119      */
57120     /**
57121      * @cfg {Number} sm (Optional)
57122      */
57123     /**
57124      * @cfg {Number} md (Optional)
57125      */
57126     /**
57127      * @cfg {Number} lg (Optional)
57128      */
57129     /**
57130      * Returns the id of the column at the specified index.
57131      * @param {Number} index The column index
57132      * @return {String} the id
57133      */
57134     getColumnId : function(index){
57135         return this.config[index].id;
57136     },
57137
57138     /**
57139      * Returns the column for a specified id.
57140      * @param {String} id The column id
57141      * @return {Object} the column
57142      */
57143     getColumnById : function(id){
57144         return this.lookup[id];
57145     },
57146
57147     
57148     /**
57149      * Returns the column for a specified dataIndex.
57150      * @param {String} dataIndex The column dataIndex
57151      * @return {Object|Boolean} the column or false if not found
57152      */
57153     getColumnByDataIndex: function(dataIndex){
57154         var index = this.findColumnIndex(dataIndex);
57155         return index > -1 ? this.config[index] : false;
57156     },
57157     
57158     /**
57159      * Returns the index for a specified column id.
57160      * @param {String} id The column id
57161      * @return {Number} the index, or -1 if not found
57162      */
57163     getIndexById : function(id){
57164         for(var i = 0, len = this.config.length; i < len; i++){
57165             if(this.config[i].id == id){
57166                 return i;
57167             }
57168         }
57169         return -1;
57170     },
57171     
57172     /**
57173      * Returns the index for a specified column dataIndex.
57174      * @param {String} dataIndex The column dataIndex
57175      * @return {Number} the index, or -1 if not found
57176      */
57177     
57178     findColumnIndex : function(dataIndex){
57179         for(var i = 0, len = this.config.length; i < len; i++){
57180             if(this.config[i].dataIndex == dataIndex){
57181                 return i;
57182             }
57183         }
57184         return -1;
57185     },
57186     
57187     
57188     moveColumn : function(oldIndex, newIndex){
57189         var c = this.config[oldIndex];
57190         this.config.splice(oldIndex, 1);
57191         this.config.splice(newIndex, 0, c);
57192         this.dataMap = null;
57193         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57194     },
57195
57196     isLocked : function(colIndex){
57197         return this.config[colIndex].locked === true;
57198     },
57199
57200     setLocked : function(colIndex, value, suppressEvent){
57201         if(this.isLocked(colIndex) == value){
57202             return;
57203         }
57204         this.config[colIndex].locked = value;
57205         if(!suppressEvent){
57206             this.fireEvent("columnlockchange", this, colIndex, value);
57207         }
57208     },
57209
57210     getTotalLockedWidth : function(){
57211         var totalWidth = 0;
57212         for(var i = 0; i < this.config.length; i++){
57213             if(this.isLocked(i) && !this.isHidden(i)){
57214                 this.totalWidth += this.getColumnWidth(i);
57215             }
57216         }
57217         return totalWidth;
57218     },
57219
57220     getLockedCount : function(){
57221         for(var i = 0, len = this.config.length; i < len; i++){
57222             if(!this.isLocked(i)){
57223                 return i;
57224             }
57225         }
57226         
57227         return this.config.length;
57228     },
57229
57230     /**
57231      * Returns the number of columns.
57232      * @return {Number}
57233      */
57234     getColumnCount : function(visibleOnly){
57235         if(visibleOnly === true){
57236             var c = 0;
57237             for(var i = 0, len = this.config.length; i < len; i++){
57238                 if(!this.isHidden(i)){
57239                     c++;
57240                 }
57241             }
57242             return c;
57243         }
57244         return this.config.length;
57245     },
57246
57247     /**
57248      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57249      * @param {Function} fn
57250      * @param {Object} scope (optional)
57251      * @return {Array} result
57252      */
57253     getColumnsBy : function(fn, scope){
57254         var r = [];
57255         for(var i = 0, len = this.config.length; i < len; i++){
57256             var c = this.config[i];
57257             if(fn.call(scope||this, c, i) === true){
57258                 r[r.length] = c;
57259             }
57260         }
57261         return r;
57262     },
57263
57264     /**
57265      * Returns true if the specified column is sortable.
57266      * @param {Number} col The column index
57267      * @return {Boolean}
57268      */
57269     isSortable : function(col){
57270         if(typeof this.config[col].sortable == "undefined"){
57271             return this.defaultSortable;
57272         }
57273         return this.config[col].sortable;
57274     },
57275
57276     /**
57277      * Returns the rendering (formatting) function defined for the column.
57278      * @param {Number} col The column index.
57279      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57280      */
57281     getRenderer : function(col){
57282         if(!this.config[col].renderer){
57283             return Roo.grid.ColumnModel.defaultRenderer;
57284         }
57285         return this.config[col].renderer;
57286     },
57287
57288     /**
57289      * Sets the rendering (formatting) function for a column.
57290      * @param {Number} col The column index
57291      * @param {Function} fn The function to use to process the cell's raw data
57292      * to return HTML markup for the grid view. The render function is called with
57293      * the following parameters:<ul>
57294      * <li>Data value.</li>
57295      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57296      * <li>css A CSS style string to apply to the table cell.</li>
57297      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57298      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57299      * <li>Row index</li>
57300      * <li>Column index</li>
57301      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57302      */
57303     setRenderer : function(col, fn){
57304         this.config[col].renderer = fn;
57305     },
57306
57307     /**
57308      * Returns the width for the specified column.
57309      * @param {Number} col The column index
57310      * @return {Number}
57311      */
57312     getColumnWidth : function(col){
57313         return this.config[col].width * 1 || this.defaultWidth;
57314     },
57315
57316     /**
57317      * Sets the width for a column.
57318      * @param {Number} col The column index
57319      * @param {Number} width The new width
57320      */
57321     setColumnWidth : function(col, width, suppressEvent){
57322         this.config[col].width = width;
57323         this.totalWidth = null;
57324         if(!suppressEvent){
57325              this.fireEvent("widthchange", this, col, width);
57326         }
57327     },
57328
57329     /**
57330      * Returns the total width of all columns.
57331      * @param {Boolean} includeHidden True to include hidden column widths
57332      * @return {Number}
57333      */
57334     getTotalWidth : function(includeHidden){
57335         if(!this.totalWidth){
57336             this.totalWidth = 0;
57337             for(var i = 0, len = this.config.length; i < len; i++){
57338                 if(includeHidden || !this.isHidden(i)){
57339                     this.totalWidth += this.getColumnWidth(i);
57340                 }
57341             }
57342         }
57343         return this.totalWidth;
57344     },
57345
57346     /**
57347      * Returns the header for the specified column.
57348      * @param {Number} col The column index
57349      * @return {String}
57350      */
57351     getColumnHeader : function(col){
57352         return this.config[col].header;
57353     },
57354
57355     /**
57356      * Sets the header for a column.
57357      * @param {Number} col The column index
57358      * @param {String} header The new header
57359      */
57360     setColumnHeader : function(col, header){
57361         this.config[col].header = header;
57362         this.fireEvent("headerchange", this, col, header);
57363     },
57364
57365     /**
57366      * Returns the tooltip for the specified column.
57367      * @param {Number} col The column index
57368      * @return {String}
57369      */
57370     getColumnTooltip : function(col){
57371             return this.config[col].tooltip;
57372     },
57373     /**
57374      * Sets the tooltip for a column.
57375      * @param {Number} col The column index
57376      * @param {String} tooltip The new tooltip
57377      */
57378     setColumnTooltip : function(col, tooltip){
57379             this.config[col].tooltip = tooltip;
57380     },
57381
57382     /**
57383      * Returns the dataIndex for the specified column.
57384      * @param {Number} col The column index
57385      * @return {Number}
57386      */
57387     getDataIndex : function(col){
57388         return this.config[col].dataIndex;
57389     },
57390
57391     /**
57392      * Sets the dataIndex for a column.
57393      * @param {Number} col The column index
57394      * @param {Number} dataIndex The new dataIndex
57395      */
57396     setDataIndex : function(col, dataIndex){
57397         this.config[col].dataIndex = dataIndex;
57398     },
57399
57400     
57401     
57402     /**
57403      * Returns true if the cell is editable.
57404      * @param {Number} colIndex The column index
57405      * @param {Number} rowIndex The row index - this is nto actually used..?
57406      * @return {Boolean}
57407      */
57408     isCellEditable : function(colIndex, rowIndex){
57409         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57410     },
57411
57412     /**
57413      * Returns the editor defined for the cell/column.
57414      * return false or null to disable editing.
57415      * @param {Number} colIndex The column index
57416      * @param {Number} rowIndex The row index
57417      * @return {Object}
57418      */
57419     getCellEditor : function(colIndex, rowIndex){
57420         return this.config[colIndex].editor;
57421     },
57422
57423     /**
57424      * Sets if a column is editable.
57425      * @param {Number} col The column index
57426      * @param {Boolean} editable True if the column is editable
57427      */
57428     setEditable : function(col, editable){
57429         this.config[col].editable = editable;
57430     },
57431
57432
57433     /**
57434      * Returns true if the column is hidden.
57435      * @param {Number} colIndex The column index
57436      * @return {Boolean}
57437      */
57438     isHidden : function(colIndex){
57439         return this.config[colIndex].hidden;
57440     },
57441
57442
57443     /**
57444      * Returns true if the column width cannot be changed
57445      */
57446     isFixed : function(colIndex){
57447         return this.config[colIndex].fixed;
57448     },
57449
57450     /**
57451      * Returns true if the column can be resized
57452      * @return {Boolean}
57453      */
57454     isResizable : function(colIndex){
57455         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57456     },
57457     /**
57458      * Sets if a column is hidden.
57459      * @param {Number} colIndex The column index
57460      * @param {Boolean} hidden True if the column is hidden
57461      */
57462     setHidden : function(colIndex, hidden){
57463         this.config[colIndex].hidden = hidden;
57464         this.totalWidth = null;
57465         this.fireEvent("hiddenchange", this, colIndex, hidden);
57466     },
57467
57468     /**
57469      * Sets the editor for a column.
57470      * @param {Number} col The column index
57471      * @param {Object} editor The editor object
57472      */
57473     setEditor : function(col, editor){
57474         this.config[col].editor = editor;
57475     }
57476 });
57477
57478 Roo.grid.ColumnModel.defaultRenderer = function(value)
57479 {
57480     if(typeof value == "object") {
57481         return value;
57482     }
57483         if(typeof value == "string" && value.length < 1){
57484             return "&#160;";
57485         }
57486     
57487         return String.format("{0}", value);
57488 };
57489
57490 // Alias for backwards compatibility
57491 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57492 /*
57493  * Based on:
57494  * Ext JS Library 1.1.1
57495  * Copyright(c) 2006-2007, Ext JS, LLC.
57496  *
57497  * Originally Released Under LGPL - original licence link has changed is not relivant.
57498  *
57499  * Fork - LGPL
57500  * <script type="text/javascript">
57501  */
57502
57503 /**
57504  * @class Roo.grid.AbstractSelectionModel
57505  * @extends Roo.util.Observable
57506  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57507  * implemented by descendant classes.  This class should not be directly instantiated.
57508  * @constructor
57509  */
57510 Roo.grid.AbstractSelectionModel = function(){
57511     this.locked = false;
57512     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57513 };
57514
57515 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57516     /** @ignore Called by the grid automatically. Do not call directly. */
57517     init : function(grid){
57518         this.grid = grid;
57519         this.initEvents();
57520     },
57521
57522     /**
57523      * Locks the selections.
57524      */
57525     lock : function(){
57526         this.locked = true;
57527     },
57528
57529     /**
57530      * Unlocks the selections.
57531      */
57532     unlock : function(){
57533         this.locked = false;
57534     },
57535
57536     /**
57537      * Returns true if the selections are locked.
57538      * @return {Boolean}
57539      */
57540     isLocked : function(){
57541         return this.locked;
57542     }
57543 });/*
57544  * Based on:
57545  * Ext JS Library 1.1.1
57546  * Copyright(c) 2006-2007, Ext JS, LLC.
57547  *
57548  * Originally Released Under LGPL - original licence link has changed is not relivant.
57549  *
57550  * Fork - LGPL
57551  * <script type="text/javascript">
57552  */
57553 /**
57554  * @extends Roo.grid.AbstractSelectionModel
57555  * @class Roo.grid.RowSelectionModel
57556  * The default SelectionModel used by {@link Roo.grid.Grid}.
57557  * It supports multiple selections and keyboard selection/navigation. 
57558  * @constructor
57559  * @param {Object} config
57560  */
57561 Roo.grid.RowSelectionModel = function(config){
57562     Roo.apply(this, config);
57563     this.selections = new Roo.util.MixedCollection(false, function(o){
57564         return o.id;
57565     });
57566
57567     this.last = false;
57568     this.lastActive = false;
57569
57570     this.addEvents({
57571         /**
57572              * @event selectionchange
57573              * Fires when the selection changes
57574              * @param {SelectionModel} this
57575              */
57576             "selectionchange" : true,
57577         /**
57578              * @event afterselectionchange
57579              * Fires after the selection changes (eg. by key press or clicking)
57580              * @param {SelectionModel} this
57581              */
57582             "afterselectionchange" : true,
57583         /**
57584              * @event beforerowselect
57585              * Fires when a row is selected being selected, return false to cancel.
57586              * @param {SelectionModel} this
57587              * @param {Number} rowIndex The selected index
57588              * @param {Boolean} keepExisting False if other selections will be cleared
57589              */
57590             "beforerowselect" : true,
57591         /**
57592              * @event rowselect
57593              * Fires when a row is selected.
57594              * @param {SelectionModel} this
57595              * @param {Number} rowIndex The selected index
57596              * @param {Roo.data.Record} r The record
57597              */
57598             "rowselect" : true,
57599         /**
57600              * @event rowdeselect
57601              * Fires when a row is deselected.
57602              * @param {SelectionModel} this
57603              * @param {Number} rowIndex The selected index
57604              */
57605         "rowdeselect" : true
57606     });
57607     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57608     this.locked = false;
57609 };
57610
57611 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57612     /**
57613      * @cfg {Boolean} singleSelect
57614      * True to allow selection of only one row at a time (defaults to false)
57615      */
57616     singleSelect : false,
57617
57618     // private
57619     initEvents : function(){
57620
57621         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57622             this.grid.on("mousedown", this.handleMouseDown, this);
57623         }else{ // allow click to work like normal
57624             this.grid.on("rowclick", this.handleDragableRowClick, this);
57625         }
57626
57627         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57628             "up" : function(e){
57629                 if(!e.shiftKey){
57630                     this.selectPrevious(e.shiftKey);
57631                 }else if(this.last !== false && this.lastActive !== false){
57632                     var last = this.last;
57633                     this.selectRange(this.last,  this.lastActive-1);
57634                     this.grid.getView().focusRow(this.lastActive);
57635                     if(last !== false){
57636                         this.last = last;
57637                     }
57638                 }else{
57639                     this.selectFirstRow();
57640                 }
57641                 this.fireEvent("afterselectionchange", this);
57642             },
57643             "down" : function(e){
57644                 if(!e.shiftKey){
57645                     this.selectNext(e.shiftKey);
57646                 }else if(this.last !== false && this.lastActive !== false){
57647                     var last = this.last;
57648                     this.selectRange(this.last,  this.lastActive+1);
57649                     this.grid.getView().focusRow(this.lastActive);
57650                     if(last !== false){
57651                         this.last = last;
57652                     }
57653                 }else{
57654                     this.selectFirstRow();
57655                 }
57656                 this.fireEvent("afterselectionchange", this);
57657             },
57658             scope: this
57659         });
57660
57661         var view = this.grid.view;
57662         view.on("refresh", this.onRefresh, this);
57663         view.on("rowupdated", this.onRowUpdated, this);
57664         view.on("rowremoved", this.onRemove, this);
57665     },
57666
57667     // private
57668     onRefresh : function(){
57669         var ds = this.grid.dataSource, i, v = this.grid.view;
57670         var s = this.selections;
57671         s.each(function(r){
57672             if((i = ds.indexOfId(r.id)) != -1){
57673                 v.onRowSelect(i);
57674                 s.add(ds.getAt(i)); // updating the selection relate data
57675             }else{
57676                 s.remove(r);
57677             }
57678         });
57679     },
57680
57681     // private
57682     onRemove : function(v, index, r){
57683         this.selections.remove(r);
57684     },
57685
57686     // private
57687     onRowUpdated : function(v, index, r){
57688         if(this.isSelected(r)){
57689             v.onRowSelect(index);
57690         }
57691     },
57692
57693     /**
57694      * Select records.
57695      * @param {Array} records The records to select
57696      * @param {Boolean} keepExisting (optional) True to keep existing selections
57697      */
57698     selectRecords : function(records, keepExisting){
57699         if(!keepExisting){
57700             this.clearSelections();
57701         }
57702         var ds = this.grid.dataSource;
57703         for(var i = 0, len = records.length; i < len; i++){
57704             this.selectRow(ds.indexOf(records[i]), true);
57705         }
57706     },
57707
57708     /**
57709      * Gets the number of selected rows.
57710      * @return {Number}
57711      */
57712     getCount : function(){
57713         return this.selections.length;
57714     },
57715
57716     /**
57717      * Selects the first row in the grid.
57718      */
57719     selectFirstRow : function(){
57720         this.selectRow(0);
57721     },
57722
57723     /**
57724      * Select the last row.
57725      * @param {Boolean} keepExisting (optional) True to keep existing selections
57726      */
57727     selectLastRow : function(keepExisting){
57728         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57729     },
57730
57731     /**
57732      * Selects the row immediately following the last selected row.
57733      * @param {Boolean} keepExisting (optional) True to keep existing selections
57734      */
57735     selectNext : function(keepExisting){
57736         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57737             this.selectRow(this.last+1, keepExisting);
57738             this.grid.getView().focusRow(this.last);
57739         }
57740     },
57741
57742     /**
57743      * Selects the row that precedes the last selected row.
57744      * @param {Boolean} keepExisting (optional) True to keep existing selections
57745      */
57746     selectPrevious : function(keepExisting){
57747         if(this.last){
57748             this.selectRow(this.last-1, keepExisting);
57749             this.grid.getView().focusRow(this.last);
57750         }
57751     },
57752
57753     /**
57754      * Returns the selected records
57755      * @return {Array} Array of selected records
57756      */
57757     getSelections : function(){
57758         return [].concat(this.selections.items);
57759     },
57760
57761     /**
57762      * Returns the first selected record.
57763      * @return {Record}
57764      */
57765     getSelected : function(){
57766         return this.selections.itemAt(0);
57767     },
57768
57769
57770     /**
57771      * Clears all selections.
57772      */
57773     clearSelections : function(fast){
57774         if(this.locked) {
57775             return;
57776         }
57777         if(fast !== true){
57778             var ds = this.grid.dataSource;
57779             var s = this.selections;
57780             s.each(function(r){
57781                 this.deselectRow(ds.indexOfId(r.id));
57782             }, this);
57783             s.clear();
57784         }else{
57785             this.selections.clear();
57786         }
57787         this.last = false;
57788     },
57789
57790
57791     /**
57792      * Selects all rows.
57793      */
57794     selectAll : function(){
57795         if(this.locked) {
57796             return;
57797         }
57798         this.selections.clear();
57799         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57800             this.selectRow(i, true);
57801         }
57802     },
57803
57804     /**
57805      * Returns True if there is a selection.
57806      * @return {Boolean}
57807      */
57808     hasSelection : function(){
57809         return this.selections.length > 0;
57810     },
57811
57812     /**
57813      * Returns True if the specified row is selected.
57814      * @param {Number/Record} record The record or index of the record to check
57815      * @return {Boolean}
57816      */
57817     isSelected : function(index){
57818         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57819         return (r && this.selections.key(r.id) ? true : false);
57820     },
57821
57822     /**
57823      * Returns True if the specified record id is selected.
57824      * @param {String} id The id of record to check
57825      * @return {Boolean}
57826      */
57827     isIdSelected : function(id){
57828         return (this.selections.key(id) ? true : false);
57829     },
57830
57831     // private
57832     handleMouseDown : function(e, t){
57833         var view = this.grid.getView(), rowIndex;
57834         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57835             return;
57836         };
57837         if(e.shiftKey && this.last !== false){
57838             var last = this.last;
57839             this.selectRange(last, rowIndex, e.ctrlKey);
57840             this.last = last; // reset the last
57841             view.focusRow(rowIndex);
57842         }else{
57843             var isSelected = this.isSelected(rowIndex);
57844             if(e.button !== 0 && isSelected){
57845                 view.focusRow(rowIndex);
57846             }else if(e.ctrlKey && isSelected){
57847                 this.deselectRow(rowIndex);
57848             }else if(!isSelected){
57849                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57850                 view.focusRow(rowIndex);
57851             }
57852         }
57853         this.fireEvent("afterselectionchange", this);
57854     },
57855     // private
57856     handleDragableRowClick :  function(grid, rowIndex, e) 
57857     {
57858         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57859             this.selectRow(rowIndex, false);
57860             grid.view.focusRow(rowIndex);
57861              this.fireEvent("afterselectionchange", this);
57862         }
57863     },
57864     
57865     /**
57866      * Selects multiple rows.
57867      * @param {Array} rows Array of the indexes of the row to select
57868      * @param {Boolean} keepExisting (optional) True to keep existing selections
57869      */
57870     selectRows : function(rows, keepExisting){
57871         if(!keepExisting){
57872             this.clearSelections();
57873         }
57874         for(var i = 0, len = rows.length; i < len; i++){
57875             this.selectRow(rows[i], true);
57876         }
57877     },
57878
57879     /**
57880      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57881      * @param {Number} startRow The index of the first row in the range
57882      * @param {Number} endRow The index of the last row in the range
57883      * @param {Boolean} keepExisting (optional) True to retain existing selections
57884      */
57885     selectRange : function(startRow, endRow, keepExisting){
57886         if(this.locked) {
57887             return;
57888         }
57889         if(!keepExisting){
57890             this.clearSelections();
57891         }
57892         if(startRow <= endRow){
57893             for(var i = startRow; i <= endRow; i++){
57894                 this.selectRow(i, true);
57895             }
57896         }else{
57897             for(var i = startRow; i >= endRow; i--){
57898                 this.selectRow(i, true);
57899             }
57900         }
57901     },
57902
57903     /**
57904      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57905      * @param {Number} startRow The index of the first row in the range
57906      * @param {Number} endRow The index of the last row in the range
57907      */
57908     deselectRange : function(startRow, endRow, preventViewNotify){
57909         if(this.locked) {
57910             return;
57911         }
57912         for(var i = startRow; i <= endRow; i++){
57913             this.deselectRow(i, preventViewNotify);
57914         }
57915     },
57916
57917     /**
57918      * Selects a row.
57919      * @param {Number} row The index of the row to select
57920      * @param {Boolean} keepExisting (optional) True to keep existing selections
57921      */
57922     selectRow : function(index, keepExisting, preventViewNotify){
57923         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57924             return;
57925         }
57926         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57927             if(!keepExisting || this.singleSelect){
57928                 this.clearSelections();
57929             }
57930             var r = this.grid.dataSource.getAt(index);
57931             this.selections.add(r);
57932             this.last = this.lastActive = index;
57933             if(!preventViewNotify){
57934                 this.grid.getView().onRowSelect(index);
57935             }
57936             this.fireEvent("rowselect", this, index, r);
57937             this.fireEvent("selectionchange", this);
57938         }
57939     },
57940
57941     /**
57942      * Deselects a row.
57943      * @param {Number} row The index of the row to deselect
57944      */
57945     deselectRow : function(index, preventViewNotify){
57946         if(this.locked) {
57947             return;
57948         }
57949         if(this.last == index){
57950             this.last = false;
57951         }
57952         if(this.lastActive == index){
57953             this.lastActive = false;
57954         }
57955         var r = this.grid.dataSource.getAt(index);
57956         this.selections.remove(r);
57957         if(!preventViewNotify){
57958             this.grid.getView().onRowDeselect(index);
57959         }
57960         this.fireEvent("rowdeselect", this, index);
57961         this.fireEvent("selectionchange", this);
57962     },
57963
57964     // private
57965     restoreLast : function(){
57966         if(this._last){
57967             this.last = this._last;
57968         }
57969     },
57970
57971     // private
57972     acceptsNav : function(row, col, cm){
57973         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57974     },
57975
57976     // private
57977     onEditorKey : function(field, e){
57978         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57979         if(k == e.TAB){
57980             e.stopEvent();
57981             ed.completeEdit();
57982             if(e.shiftKey){
57983                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57984             }else{
57985                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57986             }
57987         }else if(k == e.ENTER && !e.ctrlKey){
57988             e.stopEvent();
57989             ed.completeEdit();
57990             if(e.shiftKey){
57991                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57992             }else{
57993                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57994             }
57995         }else if(k == e.ESC){
57996             ed.cancelEdit();
57997         }
57998         if(newCell){
57999             g.startEditing(newCell[0], newCell[1]);
58000         }
58001     }
58002 });/*
58003  * Based on:
58004  * Ext JS Library 1.1.1
58005  * Copyright(c) 2006-2007, Ext JS, LLC.
58006  *
58007  * Originally Released Under LGPL - original licence link has changed is not relivant.
58008  *
58009  * Fork - LGPL
58010  * <script type="text/javascript">
58011  */
58012 /**
58013  * @class Roo.grid.CellSelectionModel
58014  * @extends Roo.grid.AbstractSelectionModel
58015  * This class provides the basic implementation for cell selection in a grid.
58016  * @constructor
58017  * @param {Object} config The object containing the configuration of this model.
58018  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58019  */
58020 Roo.grid.CellSelectionModel = function(config){
58021     Roo.apply(this, config);
58022
58023     this.selection = null;
58024
58025     this.addEvents({
58026         /**
58027              * @event beforerowselect
58028              * Fires before a cell is selected.
58029              * @param {SelectionModel} this
58030              * @param {Number} rowIndex The selected row index
58031              * @param {Number} colIndex The selected cell index
58032              */
58033             "beforecellselect" : true,
58034         /**
58035              * @event cellselect
58036              * Fires when a cell is selected.
58037              * @param {SelectionModel} this
58038              * @param {Number} rowIndex The selected row index
58039              * @param {Number} colIndex The selected cell index
58040              */
58041             "cellselect" : true,
58042         /**
58043              * @event selectionchange
58044              * Fires when the active selection changes.
58045              * @param {SelectionModel} this
58046              * @param {Object} selection null for no selection or an object (o) with two properties
58047                 <ul>
58048                 <li>o.record: the record object for the row the selection is in</li>
58049                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58050                 </ul>
58051              */
58052             "selectionchange" : true,
58053         /**
58054              * @event tabend
58055              * Fires when the tab (or enter) was pressed on the last editable cell
58056              * You can use this to trigger add new row.
58057              * @param {SelectionModel} this
58058              */
58059             "tabend" : true,
58060          /**
58061              * @event beforeeditnext
58062              * Fires before the next editable sell is made active
58063              * You can use this to skip to another cell or fire the tabend
58064              *    if you set cell to false
58065              * @param {Object} eventdata object : { cell : [ row, col ] } 
58066              */
58067             "beforeeditnext" : true
58068     });
58069     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58070 };
58071
58072 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58073     
58074     enter_is_tab: false,
58075
58076     /** @ignore */
58077     initEvents : function(){
58078         this.grid.on("mousedown", this.handleMouseDown, this);
58079         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58080         var view = this.grid.view;
58081         view.on("refresh", this.onViewChange, this);
58082         view.on("rowupdated", this.onRowUpdated, this);
58083         view.on("beforerowremoved", this.clearSelections, this);
58084         view.on("beforerowsinserted", this.clearSelections, this);
58085         if(this.grid.isEditor){
58086             this.grid.on("beforeedit", this.beforeEdit,  this);
58087         }
58088     },
58089
58090         //private
58091     beforeEdit : function(e){
58092         this.select(e.row, e.column, false, true, e.record);
58093     },
58094
58095         //private
58096     onRowUpdated : function(v, index, r){
58097         if(this.selection && this.selection.record == r){
58098             v.onCellSelect(index, this.selection.cell[1]);
58099         }
58100     },
58101
58102         //private
58103     onViewChange : function(){
58104         this.clearSelections(true);
58105     },
58106
58107         /**
58108          * Returns the currently selected cell,.
58109          * @return {Array} The selected cell (row, column) or null if none selected.
58110          */
58111     getSelectedCell : function(){
58112         return this.selection ? this.selection.cell : null;
58113     },
58114
58115     /**
58116      * Clears all selections.
58117      * @param {Boolean} true to prevent the gridview from being notified about the change.
58118      */
58119     clearSelections : function(preventNotify){
58120         var s = this.selection;
58121         if(s){
58122             if(preventNotify !== true){
58123                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58124             }
58125             this.selection = null;
58126             this.fireEvent("selectionchange", this, null);
58127         }
58128     },
58129
58130     /**
58131      * Returns true if there is a selection.
58132      * @return {Boolean}
58133      */
58134     hasSelection : function(){
58135         return this.selection ? true : false;
58136     },
58137
58138     /** @ignore */
58139     handleMouseDown : function(e, t){
58140         var v = this.grid.getView();
58141         if(this.isLocked()){
58142             return;
58143         };
58144         var row = v.findRowIndex(t);
58145         var cell = v.findCellIndex(t);
58146         if(row !== false && cell !== false){
58147             this.select(row, cell);
58148         }
58149     },
58150
58151     /**
58152      * Selects a cell.
58153      * @param {Number} rowIndex
58154      * @param {Number} collIndex
58155      */
58156     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58157         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58158             this.clearSelections();
58159             r = r || this.grid.dataSource.getAt(rowIndex);
58160             this.selection = {
58161                 record : r,
58162                 cell : [rowIndex, colIndex]
58163             };
58164             if(!preventViewNotify){
58165                 var v = this.grid.getView();
58166                 v.onCellSelect(rowIndex, colIndex);
58167                 if(preventFocus !== true){
58168                     v.focusCell(rowIndex, colIndex);
58169                 }
58170             }
58171             this.fireEvent("cellselect", this, rowIndex, colIndex);
58172             this.fireEvent("selectionchange", this, this.selection);
58173         }
58174     },
58175
58176         //private
58177     isSelectable : function(rowIndex, colIndex, cm){
58178         return !cm.isHidden(colIndex);
58179     },
58180
58181     /** @ignore */
58182     handleKeyDown : function(e){
58183         //Roo.log('Cell Sel Model handleKeyDown');
58184         if(!e.isNavKeyPress()){
58185             return;
58186         }
58187         var g = this.grid, s = this.selection;
58188         if(!s){
58189             e.stopEvent();
58190             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58191             if(cell){
58192                 this.select(cell[0], cell[1]);
58193             }
58194             return;
58195         }
58196         var sm = this;
58197         var walk = function(row, col, step){
58198             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58199         };
58200         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58201         var newCell;
58202
58203       
58204
58205         switch(k){
58206             case e.TAB:
58207                 // handled by onEditorKey
58208                 if (g.isEditor && g.editing) {
58209                     return;
58210                 }
58211                 if(e.shiftKey) {
58212                     newCell = walk(r, c-1, -1);
58213                 } else {
58214                     newCell = walk(r, c+1, 1);
58215                 }
58216                 break;
58217             
58218             case e.DOWN:
58219                newCell = walk(r+1, c, 1);
58220                 break;
58221             
58222             case e.UP:
58223                 newCell = walk(r-1, c, -1);
58224                 break;
58225             
58226             case e.RIGHT:
58227                 newCell = walk(r, c+1, 1);
58228                 break;
58229             
58230             case e.LEFT:
58231                 newCell = walk(r, c-1, -1);
58232                 break;
58233             
58234             case e.ENTER:
58235                 
58236                 if(g.isEditor && !g.editing){
58237                    g.startEditing(r, c);
58238                    e.stopEvent();
58239                    return;
58240                 }
58241                 
58242                 
58243              break;
58244         };
58245         if(newCell){
58246             this.select(newCell[0], newCell[1]);
58247             e.stopEvent();
58248             
58249         }
58250     },
58251
58252     acceptsNav : function(row, col, cm){
58253         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58254     },
58255     /**
58256      * Selects a cell.
58257      * @param {Number} field (not used) - as it's normally used as a listener
58258      * @param {Number} e - event - fake it by using
58259      *
58260      * var e = Roo.EventObjectImpl.prototype;
58261      * e.keyCode = e.TAB
58262      *
58263      * 
58264      */
58265     onEditorKey : function(field, e){
58266         
58267         var k = e.getKey(),
58268             newCell,
58269             g = this.grid,
58270             ed = g.activeEditor,
58271             forward = false;
58272         ///Roo.log('onEditorKey' + k);
58273         
58274         
58275         if (this.enter_is_tab && k == e.ENTER) {
58276             k = e.TAB;
58277         }
58278         
58279         if(k == e.TAB){
58280             if(e.shiftKey){
58281                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58282             }else{
58283                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58284                 forward = true;
58285             }
58286             
58287             e.stopEvent();
58288             
58289         } else if(k == e.ENTER &&  !e.ctrlKey){
58290             ed.completeEdit();
58291             e.stopEvent();
58292             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58293         
58294                 } else if(k == e.ESC){
58295             ed.cancelEdit();
58296         }
58297                 
58298         if (newCell) {
58299             var ecall = { cell : newCell, forward : forward };
58300             this.fireEvent('beforeeditnext', ecall );
58301             newCell = ecall.cell;
58302                         forward = ecall.forward;
58303         }
58304                 
58305         if(newCell){
58306             //Roo.log('next cell after edit');
58307             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58308         } else if (forward) {
58309             // tabbed past last
58310             this.fireEvent.defer(100, this, ['tabend',this]);
58311         }
58312     }
58313 });/*
58314  * Based on:
58315  * Ext JS Library 1.1.1
58316  * Copyright(c) 2006-2007, Ext JS, LLC.
58317  *
58318  * Originally Released Under LGPL - original licence link has changed is not relivant.
58319  *
58320  * Fork - LGPL
58321  * <script type="text/javascript">
58322  */
58323  
58324 /**
58325  * @class Roo.grid.EditorGrid
58326  * @extends Roo.grid.Grid
58327  * Class for creating and editable grid.
58328  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58329  * The container MUST have some type of size defined for the grid to fill. The container will be 
58330  * automatically set to position relative if it isn't already.
58331  * @param {Object} dataSource The data model to bind to
58332  * @param {Object} colModel The column model with info about this grid's columns
58333  */
58334 Roo.grid.EditorGrid = function(container, config){
58335     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58336     this.getGridEl().addClass("xedit-grid");
58337
58338     if(!this.selModel){
58339         this.selModel = new Roo.grid.CellSelectionModel();
58340     }
58341
58342     this.activeEditor = null;
58343
58344         this.addEvents({
58345             /**
58346              * @event beforeedit
58347              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58348              * <ul style="padding:5px;padding-left:16px;">
58349              * <li>grid - This grid</li>
58350              * <li>record - The record being edited</li>
58351              * <li>field - The field name being edited</li>
58352              * <li>value - The value for the field being edited.</li>
58353              * <li>row - The grid row index</li>
58354              * <li>column - The grid column index</li>
58355              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58356              * </ul>
58357              * @param {Object} e An edit event (see above for description)
58358              */
58359             "beforeedit" : true,
58360             /**
58361              * @event afteredit
58362              * Fires after a cell is edited. <br />
58363              * <ul style="padding:5px;padding-left:16px;">
58364              * <li>grid - This grid</li>
58365              * <li>record - The record being edited</li>
58366              * <li>field - The field name being edited</li>
58367              * <li>value - The value being set</li>
58368              * <li>originalValue - The original value for the field, before the edit.</li>
58369              * <li>row - The grid row index</li>
58370              * <li>column - The grid column index</li>
58371              * </ul>
58372              * @param {Object} e An edit event (see above for description)
58373              */
58374             "afteredit" : true,
58375             /**
58376              * @event validateedit
58377              * Fires after a cell is edited, but before the value is set in the record. 
58378          * You can use this to modify the value being set in the field, Return false
58379              * to cancel the change. The edit event object has the following properties <br />
58380              * <ul style="padding:5px;padding-left:16px;">
58381          * <li>editor - This editor</li>
58382              * <li>grid - This grid</li>
58383              * <li>record - The record being edited</li>
58384              * <li>field - The field name being edited</li>
58385              * <li>value - The value being set</li>
58386              * <li>originalValue - The original value for the field, before the edit.</li>
58387              * <li>row - The grid row index</li>
58388              * <li>column - The grid column index</li>
58389              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58390              * </ul>
58391              * @param {Object} e An edit event (see above for description)
58392              */
58393             "validateedit" : true
58394         });
58395     this.on("bodyscroll", this.stopEditing,  this);
58396     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58397 };
58398
58399 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58400     /**
58401      * @cfg {Number} clicksToEdit
58402      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58403      */
58404     clicksToEdit: 2,
58405
58406     // private
58407     isEditor : true,
58408     // private
58409     trackMouseOver: false, // causes very odd FF errors
58410
58411     onCellDblClick : function(g, row, col){
58412         this.startEditing(row, col);
58413     },
58414
58415     onEditComplete : function(ed, value, startValue){
58416         this.editing = false;
58417         this.activeEditor = null;
58418         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58419         var r = ed.record;
58420         var field = this.colModel.getDataIndex(ed.col);
58421         var e = {
58422             grid: this,
58423             record: r,
58424             field: field,
58425             originalValue: startValue,
58426             value: value,
58427             row: ed.row,
58428             column: ed.col,
58429             cancel:false,
58430             editor: ed
58431         };
58432         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58433         cell.show();
58434           
58435         if(String(value) !== String(startValue)){
58436             
58437             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58438                 r.set(field, e.value);
58439                 // if we are dealing with a combo box..
58440                 // then we also set the 'name' colum to be the displayField
58441                 if (ed.field.displayField && ed.field.name) {
58442                     r.set(ed.field.name, ed.field.el.dom.value);
58443                 }
58444                 
58445                 delete e.cancel; //?? why!!!
58446                 this.fireEvent("afteredit", e);
58447             }
58448         } else {
58449             this.fireEvent("afteredit", e); // always fire it!
58450         }
58451         this.view.focusCell(ed.row, ed.col);
58452     },
58453
58454     /**
58455      * Starts editing the specified for the specified row/column
58456      * @param {Number} rowIndex
58457      * @param {Number} colIndex
58458      */
58459     startEditing : function(row, col){
58460         this.stopEditing();
58461         if(this.colModel.isCellEditable(col, row)){
58462             this.view.ensureVisible(row, col, true);
58463           
58464             var r = this.dataSource.getAt(row);
58465             var field = this.colModel.getDataIndex(col);
58466             var cell = Roo.get(this.view.getCell(row,col));
58467             var e = {
58468                 grid: this,
58469                 record: r,
58470                 field: field,
58471                 value: r.data[field],
58472                 row: row,
58473                 column: col,
58474                 cancel:false 
58475             };
58476             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58477                 this.editing = true;
58478                 var ed = this.colModel.getCellEditor(col, row);
58479                 
58480                 if (!ed) {
58481                     return;
58482                 }
58483                 if(!ed.rendered){
58484                     ed.render(ed.parentEl || document.body);
58485                 }
58486                 ed.field.reset();
58487                
58488                 cell.hide();
58489                 
58490                 (function(){ // complex but required for focus issues in safari, ie and opera
58491                     ed.row = row;
58492                     ed.col = col;
58493                     ed.record = r;
58494                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58495                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58496                     this.activeEditor = ed;
58497                     var v = r.data[field];
58498                     ed.startEdit(this.view.getCell(row, col), v);
58499                     // combo's with 'displayField and name set
58500                     if (ed.field.displayField && ed.field.name) {
58501                         ed.field.el.dom.value = r.data[ed.field.name];
58502                     }
58503                     
58504                     
58505                 }).defer(50, this);
58506             }
58507         }
58508     },
58509         
58510     /**
58511      * Stops any active editing
58512      */
58513     stopEditing : function(){
58514         if(this.activeEditor){
58515             this.activeEditor.completeEdit();
58516         }
58517         this.activeEditor = null;
58518     },
58519         
58520          /**
58521      * Called to get grid's drag proxy text, by default returns this.ddText.
58522      * @return {String}
58523      */
58524     getDragDropText : function(){
58525         var count = this.selModel.getSelectedCell() ? 1 : 0;
58526         return String.format(this.ddText, count, count == 1 ? '' : 's');
58527     }
58528         
58529 });/*
58530  * Based on:
58531  * Ext JS Library 1.1.1
58532  * Copyright(c) 2006-2007, Ext JS, LLC.
58533  *
58534  * Originally Released Under LGPL - original licence link has changed is not relivant.
58535  *
58536  * Fork - LGPL
58537  * <script type="text/javascript">
58538  */
58539
58540 // private - not really -- you end up using it !
58541 // This is a support class used internally by the Grid components
58542
58543 /**
58544  * @class Roo.grid.GridEditor
58545  * @extends Roo.Editor
58546  * Class for creating and editable grid elements.
58547  * @param {Object} config any settings (must include field)
58548  */
58549 Roo.grid.GridEditor = function(field, config){
58550     if (!config && field.field) {
58551         config = field;
58552         field = Roo.factory(config.field, Roo.form);
58553     }
58554     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58555     field.monitorTab = false;
58556 };
58557
58558 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58559     
58560     /**
58561      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58562      */
58563     
58564     alignment: "tl-tl",
58565     autoSize: "width",
58566     hideEl : false,
58567     cls: "x-small-editor x-grid-editor",
58568     shim:false,
58569     shadow:"frame"
58570 });/*
58571  * Based on:
58572  * Ext JS Library 1.1.1
58573  * Copyright(c) 2006-2007, Ext JS, LLC.
58574  *
58575  * Originally Released Under LGPL - original licence link has changed is not relivant.
58576  *
58577  * Fork - LGPL
58578  * <script type="text/javascript">
58579  */
58580   
58581
58582   
58583 Roo.grid.PropertyRecord = Roo.data.Record.create([
58584     {name:'name',type:'string'},  'value'
58585 ]);
58586
58587
58588 Roo.grid.PropertyStore = function(grid, source){
58589     this.grid = grid;
58590     this.store = new Roo.data.Store({
58591         recordType : Roo.grid.PropertyRecord
58592     });
58593     this.store.on('update', this.onUpdate,  this);
58594     if(source){
58595         this.setSource(source);
58596     }
58597     Roo.grid.PropertyStore.superclass.constructor.call(this);
58598 };
58599
58600
58601
58602 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58603     setSource : function(o){
58604         this.source = o;
58605         this.store.removeAll();
58606         var data = [];
58607         for(var k in o){
58608             if(this.isEditableValue(o[k])){
58609                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58610             }
58611         }
58612         this.store.loadRecords({records: data}, {}, true);
58613     },
58614
58615     onUpdate : function(ds, record, type){
58616         if(type == Roo.data.Record.EDIT){
58617             var v = record.data['value'];
58618             var oldValue = record.modified['value'];
58619             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58620                 this.source[record.id] = v;
58621                 record.commit();
58622                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58623             }else{
58624                 record.reject();
58625             }
58626         }
58627     },
58628
58629     getProperty : function(row){
58630        return this.store.getAt(row);
58631     },
58632
58633     isEditableValue: function(val){
58634         if(val && val instanceof Date){
58635             return true;
58636         }else if(typeof val == 'object' || typeof val == 'function'){
58637             return false;
58638         }
58639         return true;
58640     },
58641
58642     setValue : function(prop, value){
58643         this.source[prop] = value;
58644         this.store.getById(prop).set('value', value);
58645     },
58646
58647     getSource : function(){
58648         return this.source;
58649     }
58650 });
58651
58652 Roo.grid.PropertyColumnModel = function(grid, store){
58653     this.grid = grid;
58654     var g = Roo.grid;
58655     g.PropertyColumnModel.superclass.constructor.call(this, [
58656         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58657         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58658     ]);
58659     this.store = store;
58660     this.bselect = Roo.DomHelper.append(document.body, {
58661         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58662             {tag: 'option', value: 'true', html: 'true'},
58663             {tag: 'option', value: 'false', html: 'false'}
58664         ]
58665     });
58666     Roo.id(this.bselect);
58667     var f = Roo.form;
58668     this.editors = {
58669         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58670         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58671         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58672         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58673         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58674     };
58675     this.renderCellDelegate = this.renderCell.createDelegate(this);
58676     this.renderPropDelegate = this.renderProp.createDelegate(this);
58677 };
58678
58679 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58680     
58681     
58682     nameText : 'Name',
58683     valueText : 'Value',
58684     
58685     dateFormat : 'm/j/Y',
58686     
58687     
58688     renderDate : function(dateVal){
58689         return dateVal.dateFormat(this.dateFormat);
58690     },
58691
58692     renderBool : function(bVal){
58693         return bVal ? 'true' : 'false';
58694     },
58695
58696     isCellEditable : function(colIndex, rowIndex){
58697         return colIndex == 1;
58698     },
58699
58700     getRenderer : function(col){
58701         return col == 1 ?
58702             this.renderCellDelegate : this.renderPropDelegate;
58703     },
58704
58705     renderProp : function(v){
58706         return this.getPropertyName(v);
58707     },
58708
58709     renderCell : function(val){
58710         var rv = val;
58711         if(val instanceof Date){
58712             rv = this.renderDate(val);
58713         }else if(typeof val == 'boolean'){
58714             rv = this.renderBool(val);
58715         }
58716         return Roo.util.Format.htmlEncode(rv);
58717     },
58718
58719     getPropertyName : function(name){
58720         var pn = this.grid.propertyNames;
58721         return pn && pn[name] ? pn[name] : name;
58722     },
58723
58724     getCellEditor : function(colIndex, rowIndex){
58725         var p = this.store.getProperty(rowIndex);
58726         var n = p.data['name'], val = p.data['value'];
58727         
58728         if(typeof(this.grid.customEditors[n]) == 'string'){
58729             return this.editors[this.grid.customEditors[n]];
58730         }
58731         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58732             return this.grid.customEditors[n];
58733         }
58734         if(val instanceof Date){
58735             return this.editors['date'];
58736         }else if(typeof val == 'number'){
58737             return this.editors['number'];
58738         }else if(typeof val == 'boolean'){
58739             return this.editors['boolean'];
58740         }else{
58741             return this.editors['string'];
58742         }
58743     }
58744 });
58745
58746 /**
58747  * @class Roo.grid.PropertyGrid
58748  * @extends Roo.grid.EditorGrid
58749  * This class represents the  interface of a component based property grid control.
58750  * <br><br>Usage:<pre><code>
58751  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58752       
58753  });
58754  // set any options
58755  grid.render();
58756  * </code></pre>
58757   
58758  * @constructor
58759  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58760  * The container MUST have some type of size defined for the grid to fill. The container will be
58761  * automatically set to position relative if it isn't already.
58762  * @param {Object} config A config object that sets properties on this grid.
58763  */
58764 Roo.grid.PropertyGrid = function(container, config){
58765     config = config || {};
58766     var store = new Roo.grid.PropertyStore(this);
58767     this.store = store;
58768     var cm = new Roo.grid.PropertyColumnModel(this, store);
58769     store.store.sort('name', 'ASC');
58770     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58771         ds: store.store,
58772         cm: cm,
58773         enableColLock:false,
58774         enableColumnMove:false,
58775         stripeRows:false,
58776         trackMouseOver: false,
58777         clicksToEdit:1
58778     }, config));
58779     this.getGridEl().addClass('x-props-grid');
58780     this.lastEditRow = null;
58781     this.on('columnresize', this.onColumnResize, this);
58782     this.addEvents({
58783          /**
58784              * @event beforepropertychange
58785              * Fires before a property changes (return false to stop?)
58786              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58787              * @param {String} id Record Id
58788              * @param {String} newval New Value
58789          * @param {String} oldval Old Value
58790              */
58791         "beforepropertychange": true,
58792         /**
58793              * @event propertychange
58794              * Fires after a property changes
58795              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58796              * @param {String} id Record Id
58797              * @param {String} newval New Value
58798          * @param {String} oldval Old Value
58799              */
58800         "propertychange": true
58801     });
58802     this.customEditors = this.customEditors || {};
58803 };
58804 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58805     
58806      /**
58807      * @cfg {Object} customEditors map of colnames=> custom editors.
58808      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58809      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58810      * false disables editing of the field.
58811          */
58812     
58813       /**
58814      * @cfg {Object} propertyNames map of property Names to their displayed value
58815          */
58816     
58817     render : function(){
58818         Roo.grid.PropertyGrid.superclass.render.call(this);
58819         this.autoSize.defer(100, this);
58820     },
58821
58822     autoSize : function(){
58823         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58824         if(this.view){
58825             this.view.fitColumns();
58826         }
58827     },
58828
58829     onColumnResize : function(){
58830         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58831         this.autoSize();
58832     },
58833     /**
58834      * Sets the data for the Grid
58835      * accepts a Key => Value object of all the elements avaiable.
58836      * @param {Object} data  to appear in grid.
58837      */
58838     setSource : function(source){
58839         this.store.setSource(source);
58840         //this.autoSize();
58841     },
58842     /**
58843      * Gets all the data from the grid.
58844      * @return {Object} data  data stored in grid
58845      */
58846     getSource : function(){
58847         return this.store.getSource();
58848     }
58849 });/*
58850   
58851  * Licence LGPL
58852  
58853  */
58854  
58855 /**
58856  * @class Roo.grid.Calendar
58857  * @extends Roo.util.Grid
58858  * This class extends the Grid to provide a calendar widget
58859  * <br><br>Usage:<pre><code>
58860  var grid = new Roo.grid.Calendar("my-container-id", {
58861      ds: myDataStore,
58862      cm: myColModel,
58863      selModel: mySelectionModel,
58864      autoSizeColumns: true,
58865      monitorWindowResize: false,
58866      trackMouseOver: true
58867      eventstore : real data store..
58868  });
58869  // set any options
58870  grid.render();
58871   
58872   * @constructor
58873  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58874  * The container MUST have some type of size defined for the grid to fill. The container will be
58875  * automatically set to position relative if it isn't already.
58876  * @param {Object} config A config object that sets properties on this grid.
58877  */
58878 Roo.grid.Calendar = function(container, config){
58879         // initialize the container
58880         this.container = Roo.get(container);
58881         this.container.update("");
58882         this.container.setStyle("overflow", "hidden");
58883     this.container.addClass('x-grid-container');
58884
58885     this.id = this.container.id;
58886
58887     Roo.apply(this, config);
58888     // check and correct shorthanded configs
58889     
58890     var rows = [];
58891     var d =1;
58892     for (var r = 0;r < 6;r++) {
58893         
58894         rows[r]=[];
58895         for (var c =0;c < 7;c++) {
58896             rows[r][c]= '';
58897         }
58898     }
58899     if (this.eventStore) {
58900         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58901         this.eventStore.on('load',this.onLoad, this);
58902         this.eventStore.on('beforeload',this.clearEvents, this);
58903          
58904     }
58905     
58906     this.dataSource = new Roo.data.Store({
58907             proxy: new Roo.data.MemoryProxy(rows),
58908             reader: new Roo.data.ArrayReader({}, [
58909                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58910     });
58911
58912     this.dataSource.load();
58913     this.ds = this.dataSource;
58914     this.ds.xmodule = this.xmodule || false;
58915     
58916     
58917     var cellRender = function(v,x,r)
58918     {
58919         return String.format(
58920             '<div class="fc-day  fc-widget-content"><div>' +
58921                 '<div class="fc-event-container"></div>' +
58922                 '<div class="fc-day-number">{0}</div>'+
58923                 
58924                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58925             '</div></div>', v);
58926     
58927     }
58928     
58929     
58930     this.colModel = new Roo.grid.ColumnModel( [
58931         {
58932             xtype: 'ColumnModel',
58933             xns: Roo.grid,
58934             dataIndex : 'weekday0',
58935             header : 'Sunday',
58936             renderer : cellRender
58937         },
58938         {
58939             xtype: 'ColumnModel',
58940             xns: Roo.grid,
58941             dataIndex : 'weekday1',
58942             header : 'Monday',
58943             renderer : cellRender
58944         },
58945         {
58946             xtype: 'ColumnModel',
58947             xns: Roo.grid,
58948             dataIndex : 'weekday2',
58949             header : 'Tuesday',
58950             renderer : cellRender
58951         },
58952         {
58953             xtype: 'ColumnModel',
58954             xns: Roo.grid,
58955             dataIndex : 'weekday3',
58956             header : 'Wednesday',
58957             renderer : cellRender
58958         },
58959         {
58960             xtype: 'ColumnModel',
58961             xns: Roo.grid,
58962             dataIndex : 'weekday4',
58963             header : 'Thursday',
58964             renderer : cellRender
58965         },
58966         {
58967             xtype: 'ColumnModel',
58968             xns: Roo.grid,
58969             dataIndex : 'weekday5',
58970             header : 'Friday',
58971             renderer : cellRender
58972         },
58973         {
58974             xtype: 'ColumnModel',
58975             xns: Roo.grid,
58976             dataIndex : 'weekday6',
58977             header : 'Saturday',
58978             renderer : cellRender
58979         }
58980     ]);
58981     this.cm = this.colModel;
58982     this.cm.xmodule = this.xmodule || false;
58983  
58984         
58985           
58986     //this.selModel = new Roo.grid.CellSelectionModel();
58987     //this.sm = this.selModel;
58988     //this.selModel.init(this);
58989     
58990     
58991     if(this.width){
58992         this.container.setWidth(this.width);
58993     }
58994
58995     if(this.height){
58996         this.container.setHeight(this.height);
58997     }
58998     /** @private */
58999         this.addEvents({
59000         // raw events
59001         /**
59002          * @event click
59003          * The raw click event for the entire grid.
59004          * @param {Roo.EventObject} e
59005          */
59006         "click" : true,
59007         /**
59008          * @event dblclick
59009          * The raw dblclick event for the entire grid.
59010          * @param {Roo.EventObject} e
59011          */
59012         "dblclick" : true,
59013         /**
59014          * @event contextmenu
59015          * The raw contextmenu event for the entire grid.
59016          * @param {Roo.EventObject} e
59017          */
59018         "contextmenu" : true,
59019         /**
59020          * @event mousedown
59021          * The raw mousedown event for the entire grid.
59022          * @param {Roo.EventObject} e
59023          */
59024         "mousedown" : true,
59025         /**
59026          * @event mouseup
59027          * The raw mouseup event for the entire grid.
59028          * @param {Roo.EventObject} e
59029          */
59030         "mouseup" : true,
59031         /**
59032          * @event mouseover
59033          * The raw mouseover event for the entire grid.
59034          * @param {Roo.EventObject} e
59035          */
59036         "mouseover" : true,
59037         /**
59038          * @event mouseout
59039          * The raw mouseout event for the entire grid.
59040          * @param {Roo.EventObject} e
59041          */
59042         "mouseout" : true,
59043         /**
59044          * @event keypress
59045          * The raw keypress event for the entire grid.
59046          * @param {Roo.EventObject} e
59047          */
59048         "keypress" : true,
59049         /**
59050          * @event keydown
59051          * The raw keydown event for the entire grid.
59052          * @param {Roo.EventObject} e
59053          */
59054         "keydown" : true,
59055
59056         // custom events
59057
59058         /**
59059          * @event cellclick
59060          * Fires when a cell is clicked
59061          * @param {Grid} this
59062          * @param {Number} rowIndex
59063          * @param {Number} columnIndex
59064          * @param {Roo.EventObject} e
59065          */
59066         "cellclick" : true,
59067         /**
59068          * @event celldblclick
59069          * Fires when a cell is double clicked
59070          * @param {Grid} this
59071          * @param {Number} rowIndex
59072          * @param {Number} columnIndex
59073          * @param {Roo.EventObject} e
59074          */
59075         "celldblclick" : true,
59076         /**
59077          * @event rowclick
59078          * Fires when a row is clicked
59079          * @param {Grid} this
59080          * @param {Number} rowIndex
59081          * @param {Roo.EventObject} e
59082          */
59083         "rowclick" : true,
59084         /**
59085          * @event rowdblclick
59086          * Fires when a row is double clicked
59087          * @param {Grid} this
59088          * @param {Number} rowIndex
59089          * @param {Roo.EventObject} e
59090          */
59091         "rowdblclick" : true,
59092         /**
59093          * @event headerclick
59094          * Fires when a header is clicked
59095          * @param {Grid} this
59096          * @param {Number} columnIndex
59097          * @param {Roo.EventObject} e
59098          */
59099         "headerclick" : true,
59100         /**
59101          * @event headerdblclick
59102          * Fires when a header cell is double clicked
59103          * @param {Grid} this
59104          * @param {Number} columnIndex
59105          * @param {Roo.EventObject} e
59106          */
59107         "headerdblclick" : true,
59108         /**
59109          * @event rowcontextmenu
59110          * Fires when a row is right clicked
59111          * @param {Grid} this
59112          * @param {Number} rowIndex
59113          * @param {Roo.EventObject} e
59114          */
59115         "rowcontextmenu" : true,
59116         /**
59117          * @event cellcontextmenu
59118          * Fires when a cell is right clicked
59119          * @param {Grid} this
59120          * @param {Number} rowIndex
59121          * @param {Number} cellIndex
59122          * @param {Roo.EventObject} e
59123          */
59124          "cellcontextmenu" : true,
59125         /**
59126          * @event headercontextmenu
59127          * Fires when a header is right clicked
59128          * @param {Grid} this
59129          * @param {Number} columnIndex
59130          * @param {Roo.EventObject} e
59131          */
59132         "headercontextmenu" : true,
59133         /**
59134          * @event bodyscroll
59135          * Fires when the body element is scrolled
59136          * @param {Number} scrollLeft
59137          * @param {Number} scrollTop
59138          */
59139         "bodyscroll" : true,
59140         /**
59141          * @event columnresize
59142          * Fires when the user resizes a column
59143          * @param {Number} columnIndex
59144          * @param {Number} newSize
59145          */
59146         "columnresize" : true,
59147         /**
59148          * @event columnmove
59149          * Fires when the user moves a column
59150          * @param {Number} oldIndex
59151          * @param {Number} newIndex
59152          */
59153         "columnmove" : true,
59154         /**
59155          * @event startdrag
59156          * Fires when row(s) start being dragged
59157          * @param {Grid} this
59158          * @param {Roo.GridDD} dd The drag drop object
59159          * @param {event} e The raw browser event
59160          */
59161         "startdrag" : true,
59162         /**
59163          * @event enddrag
59164          * Fires when a drag operation is complete
59165          * @param {Grid} this
59166          * @param {Roo.GridDD} dd The drag drop object
59167          * @param {event} e The raw browser event
59168          */
59169         "enddrag" : true,
59170         /**
59171          * @event dragdrop
59172          * Fires when dragged row(s) are dropped on a valid DD target
59173          * @param {Grid} this
59174          * @param {Roo.GridDD} dd The drag drop object
59175          * @param {String} targetId The target drag drop object
59176          * @param {event} e The raw browser event
59177          */
59178         "dragdrop" : true,
59179         /**
59180          * @event dragover
59181          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59182          * @param {Grid} this
59183          * @param {Roo.GridDD} dd The drag drop object
59184          * @param {String} targetId The target drag drop object
59185          * @param {event} e The raw browser event
59186          */
59187         "dragover" : true,
59188         /**
59189          * @event dragenter
59190          *  Fires when the dragged row(s) first cross another DD target while being dragged
59191          * @param {Grid} this
59192          * @param {Roo.GridDD} dd The drag drop object
59193          * @param {String} targetId The target drag drop object
59194          * @param {event} e The raw browser event
59195          */
59196         "dragenter" : true,
59197         /**
59198          * @event dragout
59199          * Fires when the dragged row(s) leave another DD target while being dragged
59200          * @param {Grid} this
59201          * @param {Roo.GridDD} dd The drag drop object
59202          * @param {String} targetId The target drag drop object
59203          * @param {event} e The raw browser event
59204          */
59205         "dragout" : true,
59206         /**
59207          * @event rowclass
59208          * Fires when a row is rendered, so you can change add a style to it.
59209          * @param {GridView} gridview   The grid view
59210          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59211          */
59212         'rowclass' : true,
59213
59214         /**
59215          * @event render
59216          * Fires when the grid is rendered
59217          * @param {Grid} grid
59218          */
59219         'render' : true,
59220             /**
59221              * @event select
59222              * Fires when a date is selected
59223              * @param {DatePicker} this
59224              * @param {Date} date The selected date
59225              */
59226         'select': true,
59227         /**
59228              * @event monthchange
59229              * Fires when the displayed month changes 
59230              * @param {DatePicker} this
59231              * @param {Date} date The selected month
59232              */
59233         'monthchange': true,
59234         /**
59235              * @event evententer
59236              * Fires when mouse over an event
59237              * @param {Calendar} this
59238              * @param {event} Event
59239              */
59240         'evententer': true,
59241         /**
59242              * @event eventleave
59243              * Fires when the mouse leaves an
59244              * @param {Calendar} this
59245              * @param {event}
59246              */
59247         'eventleave': true,
59248         /**
59249              * @event eventclick
59250              * Fires when the mouse click an
59251              * @param {Calendar} this
59252              * @param {event}
59253              */
59254         'eventclick': true,
59255         /**
59256              * @event eventrender
59257              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59258              * @param {Calendar} this
59259              * @param {data} data to be modified
59260              */
59261         'eventrender': true
59262         
59263     });
59264
59265     Roo.grid.Grid.superclass.constructor.call(this);
59266     this.on('render', function() {
59267         this.view.el.addClass('x-grid-cal'); 
59268         
59269         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59270
59271     },this);
59272     
59273     if (!Roo.grid.Calendar.style) {
59274         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59275             
59276             
59277             '.x-grid-cal .x-grid-col' :  {
59278                 height: 'auto !important',
59279                 'vertical-align': 'top'
59280             },
59281             '.x-grid-cal  .fc-event-hori' : {
59282                 height: '14px'
59283             }
59284              
59285             
59286         }, Roo.id());
59287     }
59288
59289     
59290     
59291 };
59292 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59293     /**
59294      * @cfg {Store} eventStore The store that loads events.
59295      */
59296     eventStore : 25,
59297
59298      
59299     activeDate : false,
59300     startDay : 0,
59301     autoWidth : true,
59302     monitorWindowResize : false,
59303
59304     
59305     resizeColumns : function() {
59306         var col = (this.view.el.getWidth() / 7) - 3;
59307         // loop through cols, and setWidth
59308         for(var i =0 ; i < 7 ; i++){
59309             this.cm.setColumnWidth(i, col);
59310         }
59311     },
59312      setDate :function(date) {
59313         
59314         Roo.log('setDate?');
59315         
59316         this.resizeColumns();
59317         var vd = this.activeDate;
59318         this.activeDate = date;
59319 //        if(vd && this.el){
59320 //            var t = date.getTime();
59321 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59322 //                Roo.log('using add remove');
59323 //                
59324 //                this.fireEvent('monthchange', this, date);
59325 //                
59326 //                this.cells.removeClass("fc-state-highlight");
59327 //                this.cells.each(function(c){
59328 //                   if(c.dateValue == t){
59329 //                       c.addClass("fc-state-highlight");
59330 //                       setTimeout(function(){
59331 //                            try{c.dom.firstChild.focus();}catch(e){}
59332 //                       }, 50);
59333 //                       return false;
59334 //                   }
59335 //                   return true;
59336 //                });
59337 //                return;
59338 //            }
59339 //        }
59340         
59341         var days = date.getDaysInMonth();
59342         
59343         var firstOfMonth = date.getFirstDateOfMonth();
59344         var startingPos = firstOfMonth.getDay()-this.startDay;
59345         
59346         if(startingPos < this.startDay){
59347             startingPos += 7;
59348         }
59349         
59350         var pm = date.add(Date.MONTH, -1);
59351         var prevStart = pm.getDaysInMonth()-startingPos;
59352 //        
59353         
59354         
59355         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59356         
59357         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59358         //this.cells.addClassOnOver('fc-state-hover');
59359         
59360         var cells = this.cells.elements;
59361         var textEls = this.textNodes;
59362         
59363         //Roo.each(cells, function(cell){
59364         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59365         //});
59366         
59367         days += startingPos;
59368
59369         // convert everything to numbers so it's fast
59370         var day = 86400000;
59371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59372         //Roo.log(d);
59373         //Roo.log(pm);
59374         //Roo.log(prevStart);
59375         
59376         var today = new Date().clearTime().getTime();
59377         var sel = date.clearTime().getTime();
59378         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59379         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59380         var ddMatch = this.disabledDatesRE;
59381         var ddText = this.disabledDatesText;
59382         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59383         var ddaysText = this.disabledDaysText;
59384         var format = this.format;
59385         
59386         var setCellClass = function(cal, cell){
59387             
59388             //Roo.log('set Cell Class');
59389             cell.title = "";
59390             var t = d.getTime();
59391             
59392             //Roo.log(d);
59393             
59394             
59395             cell.dateValue = t;
59396             if(t == today){
59397                 cell.className += " fc-today";
59398                 cell.className += " fc-state-highlight";
59399                 cell.title = cal.todayText;
59400             }
59401             if(t == sel){
59402                 // disable highlight in other month..
59403                 cell.className += " fc-state-highlight";
59404                 
59405             }
59406             // disabling
59407             if(t < min) {
59408                 //cell.className = " fc-state-disabled";
59409                 cell.title = cal.minText;
59410                 return;
59411             }
59412             if(t > max) {
59413                 //cell.className = " fc-state-disabled";
59414                 cell.title = cal.maxText;
59415                 return;
59416             }
59417             if(ddays){
59418                 if(ddays.indexOf(d.getDay()) != -1){
59419                     // cell.title = ddaysText;
59420                    // cell.className = " fc-state-disabled";
59421                 }
59422             }
59423             if(ddMatch && format){
59424                 var fvalue = d.dateFormat(format);
59425                 if(ddMatch.test(fvalue)){
59426                     cell.title = ddText.replace("%0", fvalue);
59427                    cell.className = " fc-state-disabled";
59428                 }
59429             }
59430             
59431             if (!cell.initialClassName) {
59432                 cell.initialClassName = cell.dom.className;
59433             }
59434             
59435             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59436         };
59437
59438         var i = 0;
59439         
59440         for(; i < startingPos; i++) {
59441             cells[i].dayName =  (++prevStart);
59442             Roo.log(textEls[i]);
59443             d.setDate(d.getDate()+1);
59444             
59445             //cells[i].className = "fc-past fc-other-month";
59446             setCellClass(this, cells[i]);
59447         }
59448         
59449         var intDay = 0;
59450         
59451         for(; i < days; i++){
59452             intDay = i - startingPos + 1;
59453             cells[i].dayName =  (intDay);
59454             d.setDate(d.getDate()+1);
59455             
59456             cells[i].className = ''; // "x-date-active";
59457             setCellClass(this, cells[i]);
59458         }
59459         var extraDays = 0;
59460         
59461         for(; i < 42; i++) {
59462             //textEls[i].innerHTML = (++extraDays);
59463             
59464             d.setDate(d.getDate()+1);
59465             cells[i].dayName = (++extraDays);
59466             cells[i].className = "fc-future fc-other-month";
59467             setCellClass(this, cells[i]);
59468         }
59469         
59470         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59471         
59472         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59473         
59474         // this will cause all the cells to mis
59475         var rows= [];
59476         var i =0;
59477         for (var r = 0;r < 6;r++) {
59478             for (var c =0;c < 7;c++) {
59479                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59480             }    
59481         }
59482         
59483         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59484         for(i=0;i<cells.length;i++) {
59485             
59486             this.cells.elements[i].dayName = cells[i].dayName ;
59487             this.cells.elements[i].className = cells[i].className;
59488             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59489             this.cells.elements[i].title = cells[i].title ;
59490             this.cells.elements[i].dateValue = cells[i].dateValue ;
59491         }
59492         
59493         
59494         
59495         
59496         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59497         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59498         
59499         ////if(totalRows != 6){
59500             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59501            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59502        // }
59503         
59504         this.fireEvent('monthchange', this, date);
59505         
59506         
59507     },
59508  /**
59509      * Returns the grid's SelectionModel.
59510      * @return {SelectionModel}
59511      */
59512     getSelectionModel : function(){
59513         if(!this.selModel){
59514             this.selModel = new Roo.grid.CellSelectionModel();
59515         }
59516         return this.selModel;
59517     },
59518
59519     load: function() {
59520         this.eventStore.load()
59521         
59522         
59523         
59524     },
59525     
59526     findCell : function(dt) {
59527         dt = dt.clearTime().getTime();
59528         var ret = false;
59529         this.cells.each(function(c){
59530             //Roo.log("check " +c.dateValue + '?=' + dt);
59531             if(c.dateValue == dt){
59532                 ret = c;
59533                 return false;
59534             }
59535             return true;
59536         });
59537         
59538         return ret;
59539     },
59540     
59541     findCells : function(rec) {
59542         var s = rec.data.start_dt.clone().clearTime().getTime();
59543        // Roo.log(s);
59544         var e= rec.data.end_dt.clone().clearTime().getTime();
59545        // Roo.log(e);
59546         var ret = [];
59547         this.cells.each(function(c){
59548              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59549             
59550             if(c.dateValue > e){
59551                 return ;
59552             }
59553             if(c.dateValue < s){
59554                 return ;
59555             }
59556             ret.push(c);
59557         });
59558         
59559         return ret;    
59560     },
59561     
59562     findBestRow: function(cells)
59563     {
59564         var ret = 0;
59565         
59566         for (var i =0 ; i < cells.length;i++) {
59567             ret  = Math.max(cells[i].rows || 0,ret);
59568         }
59569         return ret;
59570         
59571     },
59572     
59573     
59574     addItem : function(rec)
59575     {
59576         // look for vertical location slot in
59577         var cells = this.findCells(rec);
59578         
59579         rec.row = this.findBestRow(cells);
59580         
59581         // work out the location.
59582         
59583         var crow = false;
59584         var rows = [];
59585         for(var i =0; i < cells.length; i++) {
59586             if (!crow) {
59587                 crow = {
59588                     start : cells[i],
59589                     end :  cells[i]
59590                 };
59591                 continue;
59592             }
59593             if (crow.start.getY() == cells[i].getY()) {
59594                 // on same row.
59595                 crow.end = cells[i];
59596                 continue;
59597             }
59598             // different row.
59599             rows.push(crow);
59600             crow = {
59601                 start: cells[i],
59602                 end : cells[i]
59603             };
59604             
59605         }
59606         
59607         rows.push(crow);
59608         rec.els = [];
59609         rec.rows = rows;
59610         rec.cells = cells;
59611         for (var i = 0; i < cells.length;i++) {
59612             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59613             
59614         }
59615         
59616         
59617     },
59618     
59619     clearEvents: function() {
59620         
59621         if (!this.eventStore.getCount()) {
59622             return;
59623         }
59624         // reset number of rows in cells.
59625         Roo.each(this.cells.elements, function(c){
59626             c.rows = 0;
59627         });
59628         
59629         this.eventStore.each(function(e) {
59630             this.clearEvent(e);
59631         },this);
59632         
59633     },
59634     
59635     clearEvent : function(ev)
59636     {
59637         if (ev.els) {
59638             Roo.each(ev.els, function(el) {
59639                 el.un('mouseenter' ,this.onEventEnter, this);
59640                 el.un('mouseleave' ,this.onEventLeave, this);
59641                 el.remove();
59642             },this);
59643             ev.els = [];
59644         }
59645     },
59646     
59647     
59648     renderEvent : function(ev,ctr) {
59649         if (!ctr) {
59650              ctr = this.view.el.select('.fc-event-container',true).first();
59651         }
59652         
59653          
59654         this.clearEvent(ev);
59655             //code
59656        
59657         
59658         
59659         ev.els = [];
59660         var cells = ev.cells;
59661         var rows = ev.rows;
59662         this.fireEvent('eventrender', this, ev);
59663         
59664         for(var i =0; i < rows.length; i++) {
59665             
59666             cls = '';
59667             if (i == 0) {
59668                 cls += ' fc-event-start';
59669             }
59670             if ((i+1) == rows.length) {
59671                 cls += ' fc-event-end';
59672             }
59673             
59674             //Roo.log(ev.data);
59675             // how many rows should it span..
59676             var cg = this.eventTmpl.append(ctr,Roo.apply({
59677                 fccls : cls
59678                 
59679             }, ev.data) , true);
59680             
59681             
59682             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59683             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59684             cg.on('click', this.onEventClick, this, ev);
59685             
59686             ev.els.push(cg);
59687             
59688             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59689             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59690             //Roo.log(cg);
59691              
59692             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59693             cg.setWidth(ebox.right - sbox.x -2);
59694         }
59695     },
59696     
59697     renderEvents: function()
59698     {   
59699         // first make sure there is enough space..
59700         
59701         if (!this.eventTmpl) {
59702             this.eventTmpl = new Roo.Template(
59703                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59704                     '<div class="fc-event-inner">' +
59705                         '<span class="fc-event-time">{time}</span>' +
59706                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59707                     '</div>' +
59708                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59709                 '</div>'
59710             );
59711                 
59712         }
59713                
59714         
59715         
59716         this.cells.each(function(c) {
59717             //Roo.log(c.select('.fc-day-content div',true).first());
59718             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59719         });
59720         
59721         var ctr = this.view.el.select('.fc-event-container',true).first();
59722         
59723         var cls;
59724         this.eventStore.each(function(ev){
59725             
59726             this.renderEvent(ev);
59727              
59728              
59729         }, this);
59730         this.view.layout();
59731         
59732     },
59733     
59734     onEventEnter: function (e, el,event,d) {
59735         this.fireEvent('evententer', this, el, event);
59736     },
59737     
59738     onEventLeave: function (e, el,event,d) {
59739         this.fireEvent('eventleave', this, el, event);
59740     },
59741     
59742     onEventClick: function (e, el,event,d) {
59743         this.fireEvent('eventclick', this, el, event);
59744     },
59745     
59746     onMonthChange: function () {
59747         this.store.load();
59748     },
59749     
59750     onLoad: function () {
59751         
59752         //Roo.log('calendar onload');
59753 //         
59754         if(this.eventStore.getCount() > 0){
59755             
59756            
59757             
59758             this.eventStore.each(function(d){
59759                 
59760                 
59761                 // FIXME..
59762                 var add =   d.data;
59763                 if (typeof(add.end_dt) == 'undefined')  {
59764                     Roo.log("Missing End time in calendar data: ");
59765                     Roo.log(d);
59766                     return;
59767                 }
59768                 if (typeof(add.start_dt) == 'undefined')  {
59769                     Roo.log("Missing Start time in calendar data: ");
59770                     Roo.log(d);
59771                     return;
59772                 }
59773                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59774                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59775                 add.id = add.id || d.id;
59776                 add.title = add.title || '??';
59777                 
59778                 this.addItem(d);
59779                 
59780              
59781             },this);
59782         }
59783         
59784         this.renderEvents();
59785     }
59786     
59787
59788 });
59789 /*
59790  grid : {
59791                 xtype: 'Grid',
59792                 xns: Roo.grid,
59793                 listeners : {
59794                     render : function ()
59795                     {
59796                         _this.grid = this;
59797                         
59798                         if (!this.view.el.hasClass('course-timesheet')) {
59799                             this.view.el.addClass('course-timesheet');
59800                         }
59801                         if (this.tsStyle) {
59802                             this.ds.load({});
59803                             return; 
59804                         }
59805                         Roo.log('width');
59806                         Roo.log(_this.grid.view.el.getWidth());
59807                         
59808                         
59809                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59810                             '.course-timesheet .x-grid-row' : {
59811                                 height: '80px'
59812                             },
59813                             '.x-grid-row td' : {
59814                                 'vertical-align' : 0
59815                             },
59816                             '.course-edit-link' : {
59817                                 'color' : 'blue',
59818                                 'text-overflow' : 'ellipsis',
59819                                 'overflow' : 'hidden',
59820                                 'white-space' : 'nowrap',
59821                                 'cursor' : 'pointer'
59822                             },
59823                             '.sub-link' : {
59824                                 'color' : 'green'
59825                             },
59826                             '.de-act-sup-link' : {
59827                                 'color' : 'purple',
59828                                 'text-decoration' : 'line-through'
59829                             },
59830                             '.de-act-link' : {
59831                                 'color' : 'red',
59832                                 'text-decoration' : 'line-through'
59833                             },
59834                             '.course-timesheet .course-highlight' : {
59835                                 'border-top-style': 'dashed !important',
59836                                 'border-bottom-bottom': 'dashed !important'
59837                             },
59838                             '.course-timesheet .course-item' : {
59839                                 'font-family'   : 'tahoma, arial, helvetica',
59840                                 'font-size'     : '11px',
59841                                 'overflow'      : 'hidden',
59842                                 'padding-left'  : '10px',
59843                                 'padding-right' : '10px',
59844                                 'padding-top' : '10px' 
59845                             }
59846                             
59847                         }, Roo.id());
59848                                 this.ds.load({});
59849                     }
59850                 },
59851                 autoWidth : true,
59852                 monitorWindowResize : false,
59853                 cellrenderer : function(v,x,r)
59854                 {
59855                     return v;
59856                 },
59857                 sm : {
59858                     xtype: 'CellSelectionModel',
59859                     xns: Roo.grid
59860                 },
59861                 dataSource : {
59862                     xtype: 'Store',
59863                     xns: Roo.data,
59864                     listeners : {
59865                         beforeload : function (_self, options)
59866                         {
59867                             options.params = options.params || {};
59868                             options.params._month = _this.monthField.getValue();
59869                             options.params.limit = 9999;
59870                             options.params['sort'] = 'when_dt';    
59871                             options.params['dir'] = 'ASC';    
59872                             this.proxy.loadResponse = this.loadResponse;
59873                             Roo.log("load?");
59874                             //this.addColumns();
59875                         },
59876                         load : function (_self, records, options)
59877                         {
59878                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59879                                 // if you click on the translation.. you can edit it...
59880                                 var el = Roo.get(this);
59881                                 var id = el.dom.getAttribute('data-id');
59882                                 var d = el.dom.getAttribute('data-date');
59883                                 var t = el.dom.getAttribute('data-time');
59884                                 //var id = this.child('span').dom.textContent;
59885                                 
59886                                 //Roo.log(this);
59887                                 Pman.Dialog.CourseCalendar.show({
59888                                     id : id,
59889                                     when_d : d,
59890                                     when_t : t,
59891                                     productitem_active : id ? 1 : 0
59892                                 }, function() {
59893                                     _this.grid.ds.load({});
59894                                 });
59895                            
59896                            });
59897                            
59898                            _this.panel.fireEvent('resize', [ '', '' ]);
59899                         }
59900                     },
59901                     loadResponse : function(o, success, response){
59902                             // this is overridden on before load..
59903                             
59904                             Roo.log("our code?");       
59905                             //Roo.log(success);
59906                             //Roo.log(response)
59907                             delete this.activeRequest;
59908                             if(!success){
59909                                 this.fireEvent("loadexception", this, o, response);
59910                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59911                                 return;
59912                             }
59913                             var result;
59914                             try {
59915                                 result = o.reader.read(response);
59916                             }catch(e){
59917                                 Roo.log("load exception?");
59918                                 this.fireEvent("loadexception", this, o, response, e);
59919                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59920                                 return;
59921                             }
59922                             Roo.log("ready...");        
59923                             // loop through result.records;
59924                             // and set this.tdate[date] = [] << array of records..
59925                             _this.tdata  = {};
59926                             Roo.each(result.records, function(r){
59927                                 //Roo.log(r.data);
59928                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59929                                     _this.tdata[r.data.when_dt.format('j')] = [];
59930                                 }
59931                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59932                             });
59933                             
59934                             //Roo.log(_this.tdata);
59935                             
59936                             result.records = [];
59937                             result.totalRecords = 6;
59938                     
59939                             // let's generate some duumy records for the rows.
59940                             //var st = _this.dateField.getValue();
59941                             
59942                             // work out monday..
59943                             //st = st.add(Date.DAY, -1 * st.format('w'));
59944                             
59945                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59946                             
59947                             var firstOfMonth = date.getFirstDayOfMonth();
59948                             var days = date.getDaysInMonth();
59949                             var d = 1;
59950                             var firstAdded = false;
59951                             for (var i = 0; i < result.totalRecords ; i++) {
59952                                 //var d= st.add(Date.DAY, i);
59953                                 var row = {};
59954                                 var added = 0;
59955                                 for(var w = 0 ; w < 7 ; w++){
59956                                     if(!firstAdded && firstOfMonth != w){
59957                                         continue;
59958                                     }
59959                                     if(d > days){
59960                                         continue;
59961                                     }
59962                                     firstAdded = true;
59963                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59964                                     row['weekday'+w] = String.format(
59965                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59966                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59967                                                     d,
59968                                                     date.format('Y-m-')+dd
59969                                                 );
59970                                     added++;
59971                                     if(typeof(_this.tdata[d]) != 'undefined'){
59972                                         Roo.each(_this.tdata[d], function(r){
59973                                             var is_sub = '';
59974                                             var deactive = '';
59975                                             var id = r.id;
59976                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59977                                             if(r.parent_id*1>0){
59978                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59979                                                 id = r.parent_id;
59980                                             }
59981                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59982                                                 deactive = 'de-act-link';
59983                                             }
59984                                             
59985                                             row['weekday'+w] += String.format(
59986                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59987                                                     id, //0
59988                                                     r.product_id_name, //1
59989                                                     r.when_dt.format('h:ia'), //2
59990                                                     is_sub, //3
59991                                                     deactive, //4
59992                                                     desc // 5
59993                                             );
59994                                         });
59995                                     }
59996                                     d++;
59997                                 }
59998                                 
59999                                 // only do this if something added..
60000                                 if(added > 0){ 
60001                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60002                                 }
60003                                 
60004                                 
60005                                 // push it twice. (second one with an hour..
60006                                 
60007                             }
60008                             //Roo.log(result);
60009                             this.fireEvent("load", this, o, o.request.arg);
60010                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60011                         },
60012                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60013                     proxy : {
60014                         xtype: 'HttpProxy',
60015                         xns: Roo.data,
60016                         method : 'GET',
60017                         url : baseURL + '/Roo/Shop_course.php'
60018                     },
60019                     reader : {
60020                         xtype: 'JsonReader',
60021                         xns: Roo.data,
60022                         id : 'id',
60023                         fields : [
60024                             {
60025                                 'name': 'id',
60026                                 'type': 'int'
60027                             },
60028                             {
60029                                 'name': 'when_dt',
60030                                 'type': 'string'
60031                             },
60032                             {
60033                                 'name': 'end_dt',
60034                                 'type': 'string'
60035                             },
60036                             {
60037                                 'name': 'parent_id',
60038                                 'type': 'int'
60039                             },
60040                             {
60041                                 'name': 'product_id',
60042                                 'type': 'int'
60043                             },
60044                             {
60045                                 'name': 'productitem_id',
60046                                 'type': 'int'
60047                             },
60048                             {
60049                                 'name': 'guid',
60050                                 'type': 'int'
60051                             }
60052                         ]
60053                     }
60054                 },
60055                 toolbar : {
60056                     xtype: 'Toolbar',
60057                     xns: Roo,
60058                     items : [
60059                         {
60060                             xtype: 'Button',
60061                             xns: Roo.Toolbar,
60062                             listeners : {
60063                                 click : function (_self, e)
60064                                 {
60065                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60066                                     sd.setMonth(sd.getMonth()-1);
60067                                     _this.monthField.setValue(sd.format('Y-m-d'));
60068                                     _this.grid.ds.load({});
60069                                 }
60070                             },
60071                             text : "Back"
60072                         },
60073                         {
60074                             xtype: 'Separator',
60075                             xns: Roo.Toolbar
60076                         },
60077                         {
60078                             xtype: 'MonthField',
60079                             xns: Roo.form,
60080                             listeners : {
60081                                 render : function (_self)
60082                                 {
60083                                     _this.monthField = _self;
60084                                    // _this.monthField.set  today
60085                                 },
60086                                 select : function (combo, date)
60087                                 {
60088                                     _this.grid.ds.load({});
60089                                 }
60090                             },
60091                             value : (function() { return new Date(); })()
60092                         },
60093                         {
60094                             xtype: 'Separator',
60095                             xns: Roo.Toolbar
60096                         },
60097                         {
60098                             xtype: 'TextItem',
60099                             xns: Roo.Toolbar,
60100                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60101                         },
60102                         {
60103                             xtype: 'Fill',
60104                             xns: Roo.Toolbar
60105                         },
60106                         {
60107                             xtype: 'Button',
60108                             xns: Roo.Toolbar,
60109                             listeners : {
60110                                 click : function (_self, e)
60111                                 {
60112                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60113                                     sd.setMonth(sd.getMonth()+1);
60114                                     _this.monthField.setValue(sd.format('Y-m-d'));
60115                                     _this.grid.ds.load({});
60116                                 }
60117                             },
60118                             text : "Next"
60119                         }
60120                     ]
60121                 },
60122                  
60123             }
60124         };
60125         
60126         *//*
60127  * Based on:
60128  * Ext JS Library 1.1.1
60129  * Copyright(c) 2006-2007, Ext JS, LLC.
60130  *
60131  * Originally Released Under LGPL - original licence link has changed is not relivant.
60132  *
60133  * Fork - LGPL
60134  * <script type="text/javascript">
60135  */
60136  
60137 /**
60138  * @class Roo.LoadMask
60139  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60140  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60141  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60142  * element's UpdateManager load indicator and will be destroyed after the initial load.
60143  * @constructor
60144  * Create a new LoadMask
60145  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60146  * @param {Object} config The config object
60147  */
60148 Roo.LoadMask = function(el, config){
60149     this.el = Roo.get(el);
60150     Roo.apply(this, config);
60151     if(this.store){
60152         this.store.on('beforeload', this.onBeforeLoad, this);
60153         this.store.on('load', this.onLoad, this);
60154         this.store.on('loadexception', this.onLoadException, this);
60155         this.removeMask = false;
60156     }else{
60157         var um = this.el.getUpdateManager();
60158         um.showLoadIndicator = false; // disable the default indicator
60159         um.on('beforeupdate', this.onBeforeLoad, this);
60160         um.on('update', this.onLoad, this);
60161         um.on('failure', this.onLoad, this);
60162         this.removeMask = true;
60163     }
60164 };
60165
60166 Roo.LoadMask.prototype = {
60167     /**
60168      * @cfg {Boolean} removeMask
60169      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60170      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60171      */
60172     /**
60173      * @cfg {String} msg
60174      * The text to display in a centered loading message box (defaults to 'Loading...')
60175      */
60176     msg : 'Loading...',
60177     /**
60178      * @cfg {String} msgCls
60179      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60180      */
60181     msgCls : 'x-mask-loading',
60182
60183     /**
60184      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60185      * @type Boolean
60186      */
60187     disabled: false,
60188
60189     /**
60190      * Disables the mask to prevent it from being displayed
60191      */
60192     disable : function(){
60193        this.disabled = true;
60194     },
60195
60196     /**
60197      * Enables the mask so that it can be displayed
60198      */
60199     enable : function(){
60200         this.disabled = false;
60201     },
60202     
60203     onLoadException : function()
60204     {
60205         Roo.log(arguments);
60206         
60207         if (typeof(arguments[3]) != 'undefined') {
60208             Roo.MessageBox.alert("Error loading",arguments[3]);
60209         } 
60210         /*
60211         try {
60212             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60213                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60214             }   
60215         } catch(e) {
60216             
60217         }
60218         */
60219     
60220         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60221     },
60222     // private
60223     onLoad : function()
60224     {
60225         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60226     },
60227
60228     // private
60229     onBeforeLoad : function(){
60230         if(!this.disabled){
60231             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60232         }
60233     },
60234
60235     // private
60236     destroy : function(){
60237         if(this.store){
60238             this.store.un('beforeload', this.onBeforeLoad, this);
60239             this.store.un('load', this.onLoad, this);
60240             this.store.un('loadexception', this.onLoadException, this);
60241         }else{
60242             var um = this.el.getUpdateManager();
60243             um.un('beforeupdate', this.onBeforeLoad, this);
60244             um.un('update', this.onLoad, this);
60245             um.un('failure', this.onLoad, this);
60246         }
60247     }
60248 };/*
60249  * Based on:
60250  * Ext JS Library 1.1.1
60251  * Copyright(c) 2006-2007, Ext JS, LLC.
60252  *
60253  * Originally Released Under LGPL - original licence link has changed is not relivant.
60254  *
60255  * Fork - LGPL
60256  * <script type="text/javascript">
60257  */
60258
60259
60260 /**
60261  * @class Roo.XTemplate
60262  * @extends Roo.Template
60263  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60264 <pre><code>
60265 var t = new Roo.XTemplate(
60266         '&lt;select name="{name}"&gt;',
60267                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60268         '&lt;/select&gt;'
60269 );
60270  
60271 // then append, applying the master template values
60272  </code></pre>
60273  *
60274  * Supported features:
60275  *
60276  *  Tags:
60277
60278 <pre><code>
60279       {a_variable} - output encoded.
60280       {a_variable.format:("Y-m-d")} - call a method on the variable
60281       {a_variable:raw} - unencoded output
60282       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60283       {a_variable:this.method_on_template(...)} - call a method on the template object.
60284  
60285 </code></pre>
60286  *  The tpl tag:
60287 <pre><code>
60288         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60289         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60290         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60291         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60292   
60293         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60294         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60295 </code></pre>
60296  *      
60297  */
60298 Roo.XTemplate = function()
60299 {
60300     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60301     if (this.html) {
60302         this.compile();
60303     }
60304 };
60305
60306
60307 Roo.extend(Roo.XTemplate, Roo.Template, {
60308
60309     /**
60310      * The various sub templates
60311      */
60312     tpls : false,
60313     /**
60314      *
60315      * basic tag replacing syntax
60316      * WORD:WORD()
60317      *
60318      * // you can fake an object call by doing this
60319      *  x.t:(test,tesT) 
60320      * 
60321      */
60322     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60323
60324     /**
60325      * compile the template
60326      *
60327      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60328      *
60329      */
60330     compile: function()
60331     {
60332         var s = this.html;
60333      
60334         s = ['<tpl>', s, '</tpl>'].join('');
60335     
60336         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60337             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60338             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60339             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60340             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60341             m,
60342             id     = 0,
60343             tpls   = [];
60344     
60345         while(true == !!(m = s.match(re))){
60346             var forMatch   = m[0].match(nameRe),
60347                 ifMatch   = m[0].match(ifRe),
60348                 execMatch   = m[0].match(execRe),
60349                 namedMatch   = m[0].match(namedRe),
60350                 
60351                 exp  = null, 
60352                 fn   = null,
60353                 exec = null,
60354                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60355                 
60356             if (ifMatch) {
60357                 // if - puts fn into test..
60358                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60359                 if(exp){
60360                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60361                 }
60362             }
60363             
60364             if (execMatch) {
60365                 // exec - calls a function... returns empty if true is  returned.
60366                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60367                 if(exp){
60368                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60369                 }
60370             }
60371             
60372             
60373             if (name) {
60374                 // for = 
60375                 switch(name){
60376                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60377                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60378                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60379                 }
60380             }
60381             var uid = namedMatch ? namedMatch[1] : id;
60382             
60383             
60384             tpls.push({
60385                 id:     namedMatch ? namedMatch[1] : id,
60386                 target: name,
60387                 exec:   exec,
60388                 test:   fn,
60389                 body:   m[1] || ''
60390             });
60391             if (namedMatch) {
60392                 s = s.replace(m[0], '');
60393             } else { 
60394                 s = s.replace(m[0], '{xtpl'+ id + '}');
60395             }
60396             ++id;
60397         }
60398         this.tpls = [];
60399         for(var i = tpls.length-1; i >= 0; --i){
60400             this.compileTpl(tpls[i]);
60401             this.tpls[tpls[i].id] = tpls[i];
60402         }
60403         this.master = tpls[tpls.length-1];
60404         return this;
60405     },
60406     /**
60407      * same as applyTemplate, except it's done to one of the subTemplates
60408      * when using named templates, you can do:
60409      *
60410      * var str = pl.applySubTemplate('your-name', values);
60411      *
60412      * 
60413      * @param {Number} id of the template
60414      * @param {Object} values to apply to template
60415      * @param {Object} parent (normaly the instance of this object)
60416      */
60417     applySubTemplate : function(id, values, parent)
60418     {
60419         
60420         
60421         var t = this.tpls[id];
60422         
60423         
60424         try { 
60425             if(t.test && !t.test.call(this, values, parent)){
60426                 return '';
60427             }
60428         } catch(e) {
60429             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60430             Roo.log(e.toString());
60431             Roo.log(t.test);
60432             return ''
60433         }
60434         try { 
60435             
60436             if(t.exec && t.exec.call(this, values, parent)){
60437                 return '';
60438             }
60439         } catch(e) {
60440             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60441             Roo.log(e.toString());
60442             Roo.log(t.exec);
60443             return ''
60444         }
60445         try {
60446             var vs = t.target ? t.target.call(this, values, parent) : values;
60447             parent = t.target ? values : parent;
60448             if(t.target && vs instanceof Array){
60449                 var buf = [];
60450                 for(var i = 0, len = vs.length; i < len; i++){
60451                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60452                 }
60453                 return buf.join('');
60454             }
60455             return t.compiled.call(this, vs, parent);
60456         } catch (e) {
60457             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60458             Roo.log(e.toString());
60459             Roo.log(t.compiled);
60460             return '';
60461         }
60462     },
60463
60464     compileTpl : function(tpl)
60465     {
60466         var fm = Roo.util.Format;
60467         var useF = this.disableFormats !== true;
60468         var sep = Roo.isGecko ? "+" : ",";
60469         var undef = function(str) {
60470             Roo.log("Property not found :"  + str);
60471             return '';
60472         };
60473         
60474         var fn = function(m, name, format, args)
60475         {
60476             //Roo.log(arguments);
60477             args = args ? args.replace(/\\'/g,"'") : args;
60478             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60479             if (typeof(format) == 'undefined') {
60480                 format= 'htmlEncode';
60481             }
60482             if (format == 'raw' ) {
60483                 format = false;
60484             }
60485             
60486             if(name.substr(0, 4) == 'xtpl'){
60487                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60488             }
60489             
60490             // build an array of options to determine if value is undefined..
60491             
60492             // basically get 'xxxx.yyyy' then do
60493             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60494             //    (function () { Roo.log("Property not found"); return ''; })() :
60495             //    ......
60496             
60497             var udef_ar = [];
60498             var lookfor = '';
60499             Roo.each(name.split('.'), function(st) {
60500                 lookfor += (lookfor.length ? '.': '') + st;
60501                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60502             });
60503             
60504             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60505             
60506             
60507             if(format && useF){
60508                 
60509                 args = args ? ',' + args : "";
60510                  
60511                 if(format.substr(0, 5) != "this."){
60512                     format = "fm." + format + '(';
60513                 }else{
60514                     format = 'this.call("'+ format.substr(5) + '", ';
60515                     args = ", values";
60516                 }
60517                 
60518                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60519             }
60520              
60521             if (args.length) {
60522                 // called with xxyx.yuu:(test,test)
60523                 // change to ()
60524                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60525             }
60526             // raw.. - :raw modifier..
60527             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60528             
60529         };
60530         var body;
60531         // branched to use + in gecko and [].join() in others
60532         if(Roo.isGecko){
60533             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60534                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60535                     "';};};";
60536         }else{
60537             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60538             body.push(tpl.body.replace(/(\r\n|\n)/g,
60539                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60540             body.push("'].join('');};};");
60541             body = body.join('');
60542         }
60543         
60544         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60545        
60546         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60547         eval(body);
60548         
60549         return this;
60550     },
60551
60552     applyTemplate : function(values){
60553         return this.master.compiled.call(this, values, {});
60554         //var s = this.subs;
60555     },
60556
60557     apply : function(){
60558         return this.applyTemplate.apply(this, arguments);
60559     }
60560
60561  });
60562
60563 Roo.XTemplate.from = function(el){
60564     el = Roo.getDom(el);
60565     return new Roo.XTemplate(el.value || el.innerHTML);
60566 };