roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         
681                         
682                 }
683         
684     });
685
686
687 })();
688
689 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
690                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
691                 "Roo.app", "Roo.ux",
692                 "Roo.bootstrap",
693                 "Roo.bootstrap.dash");
694 /*
695  * Based on:
696  * Ext JS Library 1.1.1
697  * Copyright(c) 2006-2007, Ext JS, LLC.
698  *
699  * Originally Released Under LGPL - original licence link has changed is not relivant.
700  *
701  * Fork - LGPL
702  * <script type="text/javascript">
703  */
704
705 (function() {    
706     // wrappedn so fnCleanup is not in global scope...
707     if(Roo.isIE) {
708         function fnCleanUp() {
709             var p = Function.prototype;
710             delete p.createSequence;
711             delete p.defer;
712             delete p.createDelegate;
713             delete p.createCallback;
714             delete p.createInterceptor;
715
716             window.detachEvent("onunload", fnCleanUp);
717         }
718         window.attachEvent("onunload", fnCleanUp);
719     }
720 })();
721
722
723 /**
724  * @class Function
725  * These functions are available on every Function object (any JavaScript function).
726  */
727 Roo.apply(Function.prototype, {
728      /**
729      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
730      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
731      * Will create a function that is bound to those 2 args.
732      * @return {Function} The new function
733     */
734     createCallback : function(/*args...*/){
735         // make args available, in function below
736         var args = arguments;
737         var method = this;
738         return function() {
739             return method.apply(window, args);
740         };
741     },
742
743     /**
744      * Creates a delegate (callback) that sets the scope to obj.
745      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
746      * Will create a function that is automatically scoped to this.
747      * @param {Object} obj (optional) The object for which the scope is set
748      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
749      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
750      *                                             if a number the args are inserted at the specified position
751      * @return {Function} The new function
752      */
753     createDelegate : function(obj, args, appendArgs){
754         var method = this;
755         return function() {
756             var callArgs = args || arguments;
757             if(appendArgs === true){
758                 callArgs = Array.prototype.slice.call(arguments, 0);
759                 callArgs = callArgs.concat(args);
760             }else if(typeof appendArgs == "number"){
761                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
762                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
763                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
764             }
765             return method.apply(obj || window, callArgs);
766         };
767     },
768
769     /**
770      * Calls this function after the number of millseconds specified.
771      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
772      * @param {Object} obj (optional) The object for which the scope is set
773      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
774      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
775      *                                             if a number the args are inserted at the specified position
776      * @return {Number} The timeout id that can be used with clearTimeout
777      */
778     defer : function(millis, obj, args, appendArgs){
779         var fn = this.createDelegate(obj, args, appendArgs);
780         if(millis){
781             return setTimeout(fn, millis);
782         }
783         fn();
784         return 0;
785     },
786     /**
787      * Create a combined function call sequence of the original function + the passed function.
788      * The resulting function returns the results of the original function.
789      * The passed fcn is called with the parameters of the original function
790      * @param {Function} fcn The function to sequence
791      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
792      * @return {Function} The new function
793      */
794     createSequence : function(fcn, scope){
795         if(typeof fcn != "function"){
796             return this;
797         }
798         var method = this;
799         return function() {
800             var retval = method.apply(this || window, arguments);
801             fcn.apply(scope || this || window, arguments);
802             return retval;
803         };
804     },
805
806     /**
807      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
808      * The resulting function returns the results of the original function.
809      * The passed fcn is called with the parameters of the original function.
810      * @addon
811      * @param {Function} fcn The function to call before the original
812      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
813      * @return {Function} The new function
814      */
815     createInterceptor : function(fcn, scope){
816         if(typeof fcn != "function"){
817             return this;
818         }
819         var method = this;
820         return function() {
821             fcn.target = this;
822             fcn.method = method;
823             if(fcn.apply(scope || this || window, arguments) === false){
824                 return;
825             }
826             return method.apply(this || window, arguments);
827         };
828     }
829 });
830 /*
831  * Based on:
832  * Ext JS Library 1.1.1
833  * Copyright(c) 2006-2007, Ext JS, LLC.
834  *
835  * Originally Released Under LGPL - original licence link has changed is not relivant.
836  *
837  * Fork - LGPL
838  * <script type="text/javascript">
839  */
840
841 Roo.applyIf(String, {
842     
843     /** @scope String */
844     
845     /**
846      * Escapes the passed string for ' and \
847      * @param {String} string The string to escape
848      * @return {String} The escaped string
849      * @static
850      */
851     escape : function(string) {
852         return string.replace(/('|\\)/g, "\\$1");
853     },
854
855     /**
856      * Pads the left side of a string with a specified character.  This is especially useful
857      * for normalizing number and date strings.  Example usage:
858      * <pre><code>
859 var s = String.leftPad('123', 5, '0');
860 // s now contains the string: '00123'
861 </code></pre>
862      * @param {String} string The original string
863      * @param {Number} size The total length of the output string
864      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
865      * @return {String} The padded string
866      * @static
867      */
868     leftPad : function (val, size, ch) {
869         var result = new String(val);
870         if(ch === null || ch === undefined || ch === '') {
871             ch = " ";
872         }
873         while (result.length < size) {
874             result = ch + result;
875         }
876         return result;
877     },
878
879     /**
880      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
881      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
882      * <pre><code>
883 var cls = 'my-class', text = 'Some text';
884 var s = String.format('<div class="{0}">{1}</div>', cls, text);
885 // s now contains the string: '<div class="my-class">Some text</div>'
886 </code></pre>
887      * @param {String} string The tokenized string to be formatted
888      * @param {String} value1 The value to replace token {0}
889      * @param {String} value2 Etc...
890      * @return {String} The formatted string
891      * @static
892      */
893     format : function(format){
894         var args = Array.prototype.slice.call(arguments, 1);
895         return format.replace(/\{(\d+)\}/g, function(m, i){
896             return Roo.util.Format.htmlEncode(args[i]);
897         });
898     }
899   
900     
901 });
902
903 /**
904  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
905  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
906  * they are already different, the first value passed in is returned.  Note that this method returns the new value
907  * but does not change the current string.
908  * <pre><code>
909 // alternate sort directions
910 sort = sort.toggle('ASC', 'DESC');
911
912 // instead of conditional logic:
913 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
914 </code></pre>
915  * @param {String} value The value to compare to the current string
916  * @param {String} other The new value to use if the string already equals the first value passed in
917  * @return {String} The new value
918  */
919  
920 String.prototype.toggle = function(value, other){
921     return this == value ? other : value;
922 };
923
924
925 /**
926   * Remove invalid unicode characters from a string 
927   *
928   * @return {String} The clean string
929   */
930 String.prototype.unicodeClean = function () {
931     return this.replace(/[\s\S]/g,
932         function(character) {
933             if (character.charCodeAt()< 256) {
934               return character;
935            }
936            try {
937                 encodeURIComponent(character);
938            } catch(e) { 
939               return '';
940            }
941            return character;
942         }
943     );
944 };
945   
946 /*
947  * Based on:
948  * Ext JS Library 1.1.1
949  * Copyright(c) 2006-2007, Ext JS, LLC.
950  *
951  * Originally Released Under LGPL - original licence link has changed is not relivant.
952  *
953  * Fork - LGPL
954  * <script type="text/javascript">
955  */
956
957  /**
958  * @class Number
959  */
960 Roo.applyIf(Number.prototype, {
961     /**
962      * Checks whether or not the current number is within a desired range.  If the number is already within the
963      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
964      * exceeded.  Note that this method returns the constrained value but does not change the current number.
965      * @param {Number} min The minimum number in the range
966      * @param {Number} max The maximum number in the range
967      * @return {Number} The constrained value if outside the range, otherwise the current value
968      */
969     constrain : function(min, max){
970         return Math.min(Math.max(this, min), max);
971     }
972 });/*
973  * Based on:
974  * Ext JS Library 1.1.1
975  * Copyright(c) 2006-2007, Ext JS, LLC.
976  *
977  * Originally Released Under LGPL - original licence link has changed is not relivant.
978  *
979  * Fork - LGPL
980  * <script type="text/javascript">
981  */
982  /**
983  * @class Array
984  */
985 Roo.applyIf(Array.prototype, {
986     /**
987      * 
988      * Checks whether or not the specified object exists in the array.
989      * @param {Object} o The object to check for
990      * @return {Number} The index of o in the array (or -1 if it is not found)
991      */
992     indexOf : function(o){
993        for (var i = 0, len = this.length; i < len; i++){
994               if(this[i] == o) { return i; }
995        }
996            return -1;
997     },
998
999     /**
1000      * Removes the specified object from the array.  If the object is not found nothing happens.
1001      * @param {Object} o The object to remove
1002      */
1003     remove : function(o){
1004        var index = this.indexOf(o);
1005        if(index != -1){
1006            this.splice(index, 1);
1007        }
1008     },
1009     /**
1010      * Map (JS 1.6 compatibility)
1011      * @param {Function} function  to call
1012      */
1013     map : function(fun )
1014     {
1015         var len = this.length >>> 0;
1016         if (typeof fun != "function") {
1017             throw new TypeError();
1018         }
1019         var res = new Array(len);
1020         var thisp = arguments[1];
1021         for (var i = 0; i < len; i++)
1022         {
1023             if (i in this) {
1024                 res[i] = fun.call(thisp, this[i], i, this);
1025             }
1026         }
1027
1028         return res;
1029     },
1030     /**
1031      * equals
1032      * @param {Array} o The array to compare to
1033      * @returns {Boolean} true if the same
1034      */
1035     equals : function(b)
1036     {
1037         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1038         if (this === b) {
1039             return true;
1040          }
1041         if (b == null) {
1042             return false;
1043         }
1044         if (this.length !== b.length) {
1045             return false;
1046         }
1047       
1048         // sort?? a.sort().equals(b.sort());
1049       
1050         for (var i = 0; i < this.length; ++i) {
1051             if (this[i] !== b[i]) {
1052                 return false;
1053             }
1054         }
1055         return true;
1056     }
1057 });
1058
1059
1060  
1061 /*
1062  * Based on:
1063  * Ext JS Library 1.1.1
1064  * Copyright(c) 2006-2007, Ext JS, LLC.
1065  *
1066  * Originally Released Under LGPL - original licence link has changed is not relivant.
1067  *
1068  * Fork - LGPL
1069  * <script type="text/javascript">
1070  */
1071
1072 /**
1073  * @class Date
1074  *
1075  * The date parsing and format syntax is a subset of
1076  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1077  * supported will provide results equivalent to their PHP versions.
1078  *
1079  * Following is the list of all currently supported formats:
1080  *<pre>
1081 Sample date:
1082 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1083
1084 Format  Output      Description
1085 ------  ----------  --------------------------------------------------------------
1086   d      10         Day of the month, 2 digits with leading zeros
1087   D      Wed        A textual representation of a day, three letters
1088   j      10         Day of the month without leading zeros
1089   l      Wednesday  A full textual representation of the day of the week
1090   S      th         English ordinal day of month suffix, 2 chars (use with j)
1091   w      3          Numeric representation of the day of the week
1092   z      9          The julian date, or day of the year (0-365)
1093   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1094   F      January    A full textual representation of the month
1095   m      01         Numeric representation of a month, with leading zeros
1096   M      Jan        Month name abbreviation, three letters
1097   n      1          Numeric representation of a month, without leading zeros
1098   t      31         Number of days in the given month
1099   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1100   Y      2007       A full numeric representation of a year, 4 digits
1101   y      07         A two digit representation of a year
1102   a      pm         Lowercase Ante meridiem and Post meridiem
1103   A      PM         Uppercase Ante meridiem and Post meridiem
1104   g      3          12-hour format of an hour without leading zeros
1105   G      15         24-hour format of an hour without leading zeros
1106   h      03         12-hour format of an hour with leading zeros
1107   H      15         24-hour format of an hour with leading zeros
1108   i      05         Minutes with leading zeros
1109   s      01         Seconds, with leading zeros
1110   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1111   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1112   T      CST        Timezone setting of the machine running the code
1113   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1114 </pre>
1115  *
1116  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1117  * <pre><code>
1118 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1119 document.write(dt.format('Y-m-d'));                         //2007-01-10
1120 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1121 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
1122  </code></pre>
1123  *
1124  * Here are some standard date/time patterns that you might find helpful.  They
1125  * are not part of the source of Date.js, but to use them you can simply copy this
1126  * block of code into any script that is included after Date.js and they will also become
1127  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1128  * <pre><code>
1129 Date.patterns = {
1130     ISO8601Long:"Y-m-d H:i:s",
1131     ISO8601Short:"Y-m-d",
1132     ShortDate: "n/j/Y",
1133     LongDate: "l, F d, Y",
1134     FullDateTime: "l, F d, Y g:i:s A",
1135     MonthDay: "F d",
1136     ShortTime: "g:i A",
1137     LongTime: "g:i:s A",
1138     SortableDateTime: "Y-m-d\\TH:i:s",
1139     UniversalSortableDateTime: "Y-m-d H:i:sO",
1140     YearMonth: "F, Y"
1141 };
1142 </code></pre>
1143  *
1144  * Example usage:
1145  * <pre><code>
1146 var dt = new Date();
1147 document.write(dt.format(Date.patterns.ShortDate));
1148  </code></pre>
1149  */
1150
1151 /*
1152  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1153  * They generate precompiled functions from date formats instead of parsing and
1154  * processing the pattern every time you format a date.  These functions are available
1155  * on every Date object (any javascript function).
1156  *
1157  * The original article and download are here:
1158  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1159  *
1160  */
1161  
1162  
1163  // was in core
1164 /**
1165  Returns the number of milliseconds between this date and date
1166  @param {Date} date (optional) Defaults to now
1167  @return {Number} The diff in milliseconds
1168  @member Date getElapsed
1169  */
1170 Date.prototype.getElapsed = function(date) {
1171         return Math.abs((date || new Date()).getTime()-this.getTime());
1172 };
1173 // was in date file..
1174
1175
1176 // private
1177 Date.parseFunctions = {count:0};
1178 // private
1179 Date.parseRegexes = [];
1180 // private
1181 Date.formatFunctions = {count:0};
1182
1183 // private
1184 Date.prototype.dateFormat = function(format) {
1185     if (Date.formatFunctions[format] == null) {
1186         Date.createNewFormat(format);
1187     }
1188     var func = Date.formatFunctions[format];
1189     return this[func]();
1190 };
1191
1192
1193 /**
1194  * Formats a date given the supplied format string
1195  * @param {String} format The format string
1196  * @return {String} The formatted date
1197  * @method
1198  */
1199 Date.prototype.format = Date.prototype.dateFormat;
1200
1201 // private
1202 Date.createNewFormat = function(format) {
1203     var funcName = "format" + Date.formatFunctions.count++;
1204     Date.formatFunctions[format] = funcName;
1205     var code = "Date.prototype." + funcName + " = function(){return ";
1206     var special = false;
1207     var ch = '';
1208     for (var i = 0; i < format.length; ++i) {
1209         ch = format.charAt(i);
1210         if (!special && ch == "\\") {
1211             special = true;
1212         }
1213         else if (special) {
1214             special = false;
1215             code += "'" + String.escape(ch) + "' + ";
1216         }
1217         else {
1218             code += Date.getFormatCode(ch);
1219         }
1220     }
1221     /** eval:var:zzzzzzzzzzzzz */
1222     eval(code.substring(0, code.length - 3) + ";}");
1223 };
1224
1225 // private
1226 Date.getFormatCode = function(character) {
1227     switch (character) {
1228     case "d":
1229         return "String.leftPad(this.getDate(), 2, '0') + ";
1230     case "D":
1231         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1232     case "j":
1233         return "this.getDate() + ";
1234     case "l":
1235         return "Date.dayNames[this.getDay()] + ";
1236     case "S":
1237         return "this.getSuffix() + ";
1238     case "w":
1239         return "this.getDay() + ";
1240     case "z":
1241         return "this.getDayOfYear() + ";
1242     case "W":
1243         return "this.getWeekOfYear() + ";
1244     case "F":
1245         return "Date.monthNames[this.getMonth()] + ";
1246     case "m":
1247         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1248     case "M":
1249         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1250     case "n":
1251         return "(this.getMonth() + 1) + ";
1252     case "t":
1253         return "this.getDaysInMonth() + ";
1254     case "L":
1255         return "(this.isLeapYear() ? 1 : 0) + ";
1256     case "Y":
1257         return "this.getFullYear() + ";
1258     case "y":
1259         return "('' + this.getFullYear()).substring(2, 4) + ";
1260     case "a":
1261         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1262     case "A":
1263         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1264     case "g":
1265         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1266     case "G":
1267         return "this.getHours() + ";
1268     case "h":
1269         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1270     case "H":
1271         return "String.leftPad(this.getHours(), 2, '0') + ";
1272     case "i":
1273         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1274     case "s":
1275         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1276     case "O":
1277         return "this.getGMTOffset() + ";
1278     case "P":
1279         return "this.getGMTColonOffset() + ";
1280     case "T":
1281         return "this.getTimezone() + ";
1282     case "Z":
1283         return "(this.getTimezoneOffset() * -60) + ";
1284     default:
1285         return "'" + String.escape(character) + "' + ";
1286     }
1287 };
1288
1289 /**
1290  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1291  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1292  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1293  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1294  * string or the parse operation will fail.
1295  * Example Usage:
1296 <pre><code>
1297 //dt = Fri May 25 2007 (current date)
1298 var dt = new Date();
1299
1300 //dt = Thu May 25 2006 (today's month/day in 2006)
1301 dt = Date.parseDate("2006", "Y");
1302
1303 //dt = Sun Jan 15 2006 (all date parts specified)
1304 dt = Date.parseDate("2006-1-15", "Y-m-d");
1305
1306 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1307 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1308 </code></pre>
1309  * @param {String} input The unparsed date as a string
1310  * @param {String} format The format the date is in
1311  * @return {Date} The parsed date
1312  * @static
1313  */
1314 Date.parseDate = function(input, format) {
1315     if (Date.parseFunctions[format] == null) {
1316         Date.createParser(format);
1317     }
1318     var func = Date.parseFunctions[format];
1319     return Date[func](input);
1320 };
1321 /**
1322  * @private
1323  */
1324
1325 Date.createParser = function(format) {
1326     var funcName = "parse" + Date.parseFunctions.count++;
1327     var regexNum = Date.parseRegexes.length;
1328     var currentGroup = 1;
1329     Date.parseFunctions[format] = funcName;
1330
1331     var code = "Date." + funcName + " = function(input){\n"
1332         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1333         + "var d = new Date();\n"
1334         + "y = d.getFullYear();\n"
1335         + "m = d.getMonth();\n"
1336         + "d = d.getDate();\n"
1337         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1338         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1339         + "if (results && results.length > 0) {";
1340     var regex = "";
1341
1342     var special = false;
1343     var ch = '';
1344     for (var i = 0; i < format.length; ++i) {
1345         ch = format.charAt(i);
1346         if (!special && ch == "\\") {
1347             special = true;
1348         }
1349         else if (special) {
1350             special = false;
1351             regex += String.escape(ch);
1352         }
1353         else {
1354             var obj = Date.formatCodeToRegex(ch, currentGroup);
1355             currentGroup += obj.g;
1356             regex += obj.s;
1357             if (obj.g && obj.c) {
1358                 code += obj.c;
1359             }
1360         }
1361     }
1362
1363     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1364         + "{v = new Date(y, m, d, h, i, s);}\n"
1365         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1366         + "{v = new Date(y, m, d, h, i);}\n"
1367         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1368         + "{v = new Date(y, m, d, h);}\n"
1369         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1370         + "{v = new Date(y, m, d);}\n"
1371         + "else if (y >= 0 && m >= 0)\n"
1372         + "{v = new Date(y, m);}\n"
1373         + "else if (y >= 0)\n"
1374         + "{v = new Date(y);}\n"
1375         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1376         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1377         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1378         + ";}";
1379
1380     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1381     /** eval:var:zzzzzzzzzzzzz */
1382     eval(code);
1383 };
1384
1385 // private
1386 Date.formatCodeToRegex = function(character, currentGroup) {
1387     switch (character) {
1388     case "D":
1389         return {g:0,
1390         c:null,
1391         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1392     case "j":
1393         return {g:1,
1394             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1395             s:"(\\d{1,2})"}; // day of month without leading zeroes
1396     case "d":
1397         return {g:1,
1398             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1399             s:"(\\d{2})"}; // day of month with leading zeroes
1400     case "l":
1401         return {g:0,
1402             c:null,
1403             s:"(?:" + Date.dayNames.join("|") + ")"};
1404     case "S":
1405         return {g:0,
1406             c:null,
1407             s:"(?:st|nd|rd|th)"};
1408     case "w":
1409         return {g:0,
1410             c:null,
1411             s:"\\d"};
1412     case "z":
1413         return {g:0,
1414             c:null,
1415             s:"(?:\\d{1,3})"};
1416     case "W":
1417         return {g:0,
1418             c:null,
1419             s:"(?:\\d{2})"};
1420     case "F":
1421         return {g:1,
1422             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1423             s:"(" + Date.monthNames.join("|") + ")"};
1424     case "M":
1425         return {g:1,
1426             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1427             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1428     case "n":
1429         return {g:1,
1430             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1431             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1432     case "m":
1433         return {g:1,
1434             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1435             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1436     case "t":
1437         return {g:0,
1438             c:null,
1439             s:"\\d{1,2}"};
1440     case "L":
1441         return {g:0,
1442             c:null,
1443             s:"(?:1|0)"};
1444     case "Y":
1445         return {g:1,
1446             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1447             s:"(\\d{4})"};
1448     case "y":
1449         return {g:1,
1450             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1451                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1452             s:"(\\d{1,2})"};
1453     case "a":
1454         return {g:1,
1455             c:"if (results[" + currentGroup + "] == 'am') {\n"
1456                 + "if (h == 12) { h = 0; }\n"
1457                 + "} else { if (h < 12) { h += 12; }}",
1458             s:"(am|pm)"};
1459     case "A":
1460         return {g:1,
1461             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1462                 + "if (h == 12) { h = 0; }\n"
1463                 + "} else { if (h < 12) { h += 12; }}",
1464             s:"(AM|PM)"};
1465     case "g":
1466     case "G":
1467         return {g:1,
1468             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1469             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1470     case "h":
1471     case "H":
1472         return {g:1,
1473             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1474             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1475     case "i":
1476         return {g:1,
1477             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{2})"};
1479     case "s":
1480         return {g:1,
1481             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1482             s:"(\\d{2})"};
1483     case "O":
1484         return {g:1,
1485             c:[
1486                 "o = results[", currentGroup, "];\n",
1487                 "var sn = o.substring(0,1);\n", // get + / - sign
1488                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1489                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1490                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1491                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1492             ].join(""),
1493             s:"([+\-]\\d{2,4})"};
1494     
1495     
1496     case "P":
1497         return {g:1,
1498                 c:[
1499                    "o = results[", currentGroup, "];\n",
1500                    "var sn = o.substring(0,1);\n",
1501                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1502                    "var mn = o.substring(4,6) % 60;\n",
1503                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1504                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1505             ].join(""),
1506             s:"([+\-]\\d{4})"};
1507     case "T":
1508         return {g:0,
1509             c:null,
1510             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1511     case "Z":
1512         return {g:1,
1513             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1514                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1515             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1516     default:
1517         return {g:0,
1518             c:null,
1519             s:String.escape(character)};
1520     }
1521 };
1522
1523 /**
1524  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1525  * @return {String} The abbreviated timezone name (e.g. 'CST')
1526  */
1527 Date.prototype.getTimezone = function() {
1528     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1529 };
1530
1531 /**
1532  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1533  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1534  */
1535 Date.prototype.getGMTOffset = function() {
1536     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1539 };
1540
1541 /**
1542  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1543  * @return {String} 2-characters representing hours and 2-characters representing minutes
1544  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1545  */
1546 Date.prototype.getGMTColonOffset = function() {
1547         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1548                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1549                 + ":"
1550                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1551 }
1552
1553 /**
1554  * Get the numeric day number of the year, adjusted for leap year.
1555  * @return {Number} 0 through 364 (365 in leap years)
1556  */
1557 Date.prototype.getDayOfYear = function() {
1558     var num = 0;
1559     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1560     for (var i = 0; i < this.getMonth(); ++i) {
1561         num += Date.daysInMonth[i];
1562     }
1563     return num + this.getDate() - 1;
1564 };
1565
1566 /**
1567  * Get the string representation of the numeric week number of the year
1568  * (equivalent to the format specifier 'W').
1569  * @return {String} '00' through '52'
1570  */
1571 Date.prototype.getWeekOfYear = function() {
1572     // Skip to Thursday of this week
1573     var now = this.getDayOfYear() + (4 - this.getDay());
1574     // Find the first Thursday of the year
1575     var jan1 = new Date(this.getFullYear(), 0, 1);
1576     var then = (7 - jan1.getDay() + 4);
1577     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1578 };
1579
1580 /**
1581  * Whether or not the current date is in a leap year.
1582  * @return {Boolean} True if the current date is in a leap year, else false
1583  */
1584 Date.prototype.isLeapYear = function() {
1585     var year = this.getFullYear();
1586     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1587 };
1588
1589 /**
1590  * Get the first day of the current month, adjusted for leap year.  The returned value
1591  * is the numeric day index within the week (0-6) which can be used in conjunction with
1592  * the {@link #monthNames} array to retrieve the textual day name.
1593  * Example:
1594  *<pre><code>
1595 var dt = new Date('1/10/2007');
1596 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1597 </code></pre>
1598  * @return {Number} The day number (0-6)
1599  */
1600 Date.prototype.getFirstDayOfMonth = function() {
1601     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1602     return (day < 0) ? (day + 7) : day;
1603 };
1604
1605 /**
1606  * Get the last day of the current month, adjusted for leap year.  The returned value
1607  * is the numeric day index within the week (0-6) which can be used in conjunction with
1608  * the {@link #monthNames} array to retrieve the textual day name.
1609  * Example:
1610  *<pre><code>
1611 var dt = new Date('1/10/2007');
1612 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1613 </code></pre>
1614  * @return {Number} The day number (0-6)
1615  */
1616 Date.prototype.getLastDayOfMonth = function() {
1617     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1618     return (day < 0) ? (day + 7) : day;
1619 };
1620
1621
1622 /**
1623  * Get the first date of this date's month
1624  * @return {Date}
1625  */
1626 Date.prototype.getFirstDateOfMonth = function() {
1627     return new Date(this.getFullYear(), this.getMonth(), 1);
1628 };
1629
1630 /**
1631  * Get the last date of this date's month
1632  * @return {Date}
1633  */
1634 Date.prototype.getLastDateOfMonth = function() {
1635     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1636 };
1637 /**
1638  * Get the number of days in the current month, adjusted for leap year.
1639  * @return {Number} The number of days in the month
1640  */
1641 Date.prototype.getDaysInMonth = function() {
1642     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1643     return Date.daysInMonth[this.getMonth()];
1644 };
1645
1646 /**
1647  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1648  * @return {String} 'st, 'nd', 'rd' or 'th'
1649  */
1650 Date.prototype.getSuffix = function() {
1651     switch (this.getDate()) {
1652         case 1:
1653         case 21:
1654         case 31:
1655             return "st";
1656         case 2:
1657         case 22:
1658             return "nd";
1659         case 3:
1660         case 23:
1661             return "rd";
1662         default:
1663             return "th";
1664     }
1665 };
1666
1667 // private
1668 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1669
1670 /**
1671  * An array of textual month names.
1672  * Override these values for international dates, for example...
1673  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1674  * @type Array
1675  * @static
1676  */
1677 Date.monthNames =
1678    ["January",
1679     "February",
1680     "March",
1681     "April",
1682     "May",
1683     "June",
1684     "July",
1685     "August",
1686     "September",
1687     "October",
1688     "November",
1689     "December"];
1690
1691 /**
1692  * An array of textual day names.
1693  * Override these values for international dates, for example...
1694  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1695  * @type Array
1696  * @static
1697  */
1698 Date.dayNames =
1699    ["Sunday",
1700     "Monday",
1701     "Tuesday",
1702     "Wednesday",
1703     "Thursday",
1704     "Friday",
1705     "Saturday"];
1706
1707 // private
1708 Date.y2kYear = 50;
1709 // private
1710 Date.monthNumbers = {
1711     Jan:0,
1712     Feb:1,
1713     Mar:2,
1714     Apr:3,
1715     May:4,
1716     Jun:5,
1717     Jul:6,
1718     Aug:7,
1719     Sep:8,
1720     Oct:9,
1721     Nov:10,
1722     Dec:11};
1723
1724 /**
1725  * Creates and returns a new Date instance with the exact same date value as the called instance.
1726  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1727  * variable will also be changed.  When the intention is to create a new variable that will not
1728  * modify the original instance, you should create a clone.
1729  *
1730  * Example of correctly cloning a date:
1731  * <pre><code>
1732 //wrong way:
1733 var orig = new Date('10/1/2006');
1734 var copy = orig;
1735 copy.setDate(5);
1736 document.write(orig);  //returns 'Thu Oct 05 2006'!
1737
1738 //correct way:
1739 var orig = new Date('10/1/2006');
1740 var copy = orig.clone();
1741 copy.setDate(5);
1742 document.write(orig);  //returns 'Thu Oct 01 2006'
1743 </code></pre>
1744  * @return {Date} The new Date instance
1745  */
1746 Date.prototype.clone = function() {
1747         return new Date(this.getTime());
1748 };
1749
1750 /**
1751  * Clears any time information from this date
1752  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1753  @return {Date} this or the clone
1754  */
1755 Date.prototype.clearTime = function(clone){
1756     if(clone){
1757         return this.clone().clearTime();
1758     }
1759     this.setHours(0);
1760     this.setMinutes(0);
1761     this.setSeconds(0);
1762     this.setMilliseconds(0);
1763     return this;
1764 };
1765
1766 // private
1767 // safari setMonth is broken -- check that this is only donw once...
1768 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1769     Date.brokenSetMonth = Date.prototype.setMonth;
1770         Date.prototype.setMonth = function(num){
1771                 if(num <= -1){
1772                         var n = Math.ceil(-num);
1773                         var back_year = Math.ceil(n/12);
1774                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1775                         this.setFullYear(this.getFullYear() - back_year);
1776                         return Date.brokenSetMonth.call(this, month);
1777                 } else {
1778                         return Date.brokenSetMonth.apply(this, arguments);
1779                 }
1780         };
1781 }
1782
1783 /** Date interval constant 
1784 * @static 
1785 * @type String */
1786 Date.MILLI = "ms";
1787 /** Date interval constant 
1788 * @static 
1789 * @type String */
1790 Date.SECOND = "s";
1791 /** Date interval constant 
1792 * @static 
1793 * @type String */
1794 Date.MINUTE = "mi";
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.HOUR = "h";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.DAY = "d";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MONTH = "mo";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.YEAR = "y";
1811
1812 /**
1813  * Provides a convenient method of performing basic date arithmetic.  This method
1814  * does not modify the Date instance being called - it creates and returns
1815  * a new Date instance containing the resulting date value.
1816  *
1817  * Examples:
1818  * <pre><code>
1819 //Basic usage:
1820 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1821 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1822
1823 //Negative values will subtract correctly:
1824 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1825 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1826
1827 //You can even chain several calls together in one line!
1828 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1829 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1830  </code></pre>
1831  *
1832  * @param {String} interval   A valid date interval enum value
1833  * @param {Number} value      The amount to add to the current date
1834  * @return {Date} The new Date instance
1835  */
1836 Date.prototype.add = function(interval, value){
1837   var d = this.clone();
1838   if (!interval || value === 0) { return d; }
1839   switch(interval.toLowerCase()){
1840     case Date.MILLI:
1841       d.setMilliseconds(this.getMilliseconds() + value);
1842       break;
1843     case Date.SECOND:
1844       d.setSeconds(this.getSeconds() + value);
1845       break;
1846     case Date.MINUTE:
1847       d.setMinutes(this.getMinutes() + value);
1848       break;
1849     case Date.HOUR:
1850       d.setHours(this.getHours() + value);
1851       break;
1852     case Date.DAY:
1853       d.setDate(this.getDate() + value);
1854       break;
1855     case Date.MONTH:
1856       var day = this.getDate();
1857       if(day > 28){
1858           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1859       }
1860       d.setDate(day);
1861       d.setMonth(this.getMonth() + value);
1862       break;
1863     case Date.YEAR:
1864       d.setFullYear(this.getFullYear() + value);
1865       break;
1866   }
1867   return d;
1868 };
1869 /*
1870  * Based on:
1871  * Ext JS Library 1.1.1
1872  * Copyright(c) 2006-2007, Ext JS, LLC.
1873  *
1874  * Originally Released Under LGPL - original licence link has changed is not relivant.
1875  *
1876  * Fork - LGPL
1877  * <script type="text/javascript">
1878  */
1879
1880 /**
1881  * @class Roo.lib.Dom
1882  * @static
1883  * 
1884  * Dom utils (from YIU afaik)
1885  * 
1886  **/
1887 Roo.lib.Dom = {
1888     /**
1889      * Get the view width
1890      * @param {Boolean} full True will get the full document, otherwise it's the view width
1891      * @return {Number} The width
1892      */
1893      
1894     getViewWidth : function(full) {
1895         return full ? this.getDocumentWidth() : this.getViewportWidth();
1896     },
1897     /**
1898      * Get the view height
1899      * @param {Boolean} full True will get the full document, otherwise it's the view height
1900      * @return {Number} The height
1901      */
1902     getViewHeight : function(full) {
1903         return full ? this.getDocumentHeight() : this.getViewportHeight();
1904     },
1905
1906     getDocumentHeight: function() {
1907         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1908         return Math.max(scrollHeight, this.getViewportHeight());
1909     },
1910
1911     getDocumentWidth: function() {
1912         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1913         return Math.max(scrollWidth, this.getViewportWidth());
1914     },
1915
1916     getViewportHeight: function() {
1917         var height = self.innerHeight;
1918         var mode = document.compatMode;
1919
1920         if ((mode || Roo.isIE) && !Roo.isOpera) {
1921             height = (mode == "CSS1Compat") ?
1922                      document.documentElement.clientHeight :
1923                      document.body.clientHeight;
1924         }
1925
1926         return height;
1927     },
1928
1929     getViewportWidth: function() {
1930         var width = self.innerWidth;
1931         var mode = document.compatMode;
1932
1933         if (mode || Roo.isIE) {
1934             width = (mode == "CSS1Compat") ?
1935                     document.documentElement.clientWidth :
1936                     document.body.clientWidth;
1937         }
1938         return width;
1939     },
1940
1941     isAncestor : function(p, c) {
1942         p = Roo.getDom(p);
1943         c = Roo.getDom(c);
1944         if (!p || !c) {
1945             return false;
1946         }
1947
1948         if (p.contains && !Roo.isSafari) {
1949             return p.contains(c);
1950         } else if (p.compareDocumentPosition) {
1951             return !!(p.compareDocumentPosition(c) & 16);
1952         } else {
1953             var parent = c.parentNode;
1954             while (parent) {
1955                 if (parent == p) {
1956                     return true;
1957                 }
1958                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1959                     return false;
1960                 }
1961                 parent = parent.parentNode;
1962             }
1963             return false;
1964         }
1965     },
1966
1967     getRegion : function(el) {
1968         return Roo.lib.Region.getRegion(el);
1969     },
1970
1971     getY : function(el) {
1972         return this.getXY(el)[1];
1973     },
1974
1975     getX : function(el) {
1976         return this.getXY(el)[0];
1977     },
1978
1979     getXY : function(el) {
1980         var p, pe, b, scroll, bd = document.body;
1981         el = Roo.getDom(el);
1982         var fly = Roo.lib.AnimBase.fly;
1983         if (el.getBoundingClientRect) {
1984             b = el.getBoundingClientRect();
1985             scroll = fly(document).getScroll();
1986             return [b.left + scroll.left, b.top + scroll.top];
1987         }
1988         var x = 0, y = 0;
1989
1990         p = el;
1991
1992         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1993
1994         while (p) {
1995
1996             x += p.offsetLeft;
1997             y += p.offsetTop;
1998
1999             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2000                 hasAbsolute = true;
2001             }
2002
2003             if (Roo.isGecko) {
2004                 pe = fly(p);
2005
2006                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2007                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2008
2009
2010                 x += bl;
2011                 y += bt;
2012
2013
2014                 if (p != el && pe.getStyle('overflow') != 'visible') {
2015                     x += bl;
2016                     y += bt;
2017                 }
2018             }
2019             p = p.offsetParent;
2020         }
2021
2022         if (Roo.isSafari && hasAbsolute) {
2023             x -= bd.offsetLeft;
2024             y -= bd.offsetTop;
2025         }
2026
2027         if (Roo.isGecko && !hasAbsolute) {
2028             var dbd = fly(bd);
2029             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2030             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2031         }
2032
2033         p = el.parentNode;
2034         while (p && p != bd) {
2035             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2036                 x -= p.scrollLeft;
2037                 y -= p.scrollTop;
2038             }
2039             p = p.parentNode;
2040         }
2041         return [x, y];
2042     },
2043  
2044   
2045
2046
2047     setXY : function(el, xy) {
2048         el = Roo.fly(el, '_setXY');
2049         el.position();
2050         var pts = el.translatePoints(xy);
2051         if (xy[0] !== false) {
2052             el.dom.style.left = pts.left + "px";
2053         }
2054         if (xy[1] !== false) {
2055             el.dom.style.top = pts.top + "px";
2056         }
2057     },
2058
2059     setX : function(el, x) {
2060         this.setXY(el, [x, false]);
2061     },
2062
2063     setY : function(el, y) {
2064         this.setXY(el, [false, y]);
2065     }
2066 };
2067 /*
2068  * Portions of this file are based on pieces of Yahoo User Interface Library
2069  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2070  * YUI licensed under the BSD License:
2071  * http://developer.yahoo.net/yui/license.txt
2072  * <script type="text/javascript">
2073  *
2074  */
2075
2076 Roo.lib.Event = function() {
2077     var loadComplete = false;
2078     var listeners = [];
2079     var unloadListeners = [];
2080     var retryCount = 0;
2081     var onAvailStack = [];
2082     var counter = 0;
2083     var lastError = null;
2084
2085     return {
2086         POLL_RETRYS: 200,
2087         POLL_INTERVAL: 20,
2088         EL: 0,
2089         TYPE: 1,
2090         FN: 2,
2091         WFN: 3,
2092         OBJ: 3,
2093         ADJ_SCOPE: 4,
2094         _interval: null,
2095
2096         startInterval: function() {
2097             if (!this._interval) {
2098                 var self = this;
2099                 var callback = function() {
2100                     self._tryPreloadAttach();
2101                 };
2102                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2103
2104             }
2105         },
2106
2107         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2108             onAvailStack.push({ id:         p_id,
2109                 fn:         p_fn,
2110                 obj:        p_obj,
2111                 override:   p_override,
2112                 checkReady: false    });
2113
2114             retryCount = this.POLL_RETRYS;
2115             this.startInterval();
2116         },
2117
2118
2119         addListener: function(el, eventName, fn) {
2120             el = Roo.getDom(el);
2121             if (!el || !fn) {
2122                 return false;
2123             }
2124
2125             if ("unload" == eventName) {
2126                 unloadListeners[unloadListeners.length] =
2127                 [el, eventName, fn];
2128                 return true;
2129             }
2130
2131             var wrappedFn = function(e) {
2132                 return fn(Roo.lib.Event.getEvent(e));
2133             };
2134
2135             var li = [el, eventName, fn, wrappedFn];
2136
2137             var index = listeners.length;
2138             listeners[index] = li;
2139
2140             this.doAdd(el, eventName, wrappedFn, false);
2141             return true;
2142
2143         },
2144
2145
2146         removeListener: function(el, eventName, fn) {
2147             var i, len;
2148
2149             el = Roo.getDom(el);
2150
2151             if(!fn) {
2152                 return this.purgeElement(el, false, eventName);
2153             }
2154
2155
2156             if ("unload" == eventName) {
2157
2158                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2159                     var li = unloadListeners[i];
2160                     if (li &&
2161                         li[0] == el &&
2162                         li[1] == eventName &&
2163                         li[2] == fn) {
2164                         unloadListeners.splice(i, 1);
2165                         return true;
2166                     }
2167                 }
2168
2169                 return false;
2170             }
2171
2172             var cacheItem = null;
2173
2174
2175             var index = arguments[3];
2176
2177             if ("undefined" == typeof index) {
2178                 index = this._getCacheIndex(el, eventName, fn);
2179             }
2180
2181             if (index >= 0) {
2182                 cacheItem = listeners[index];
2183             }
2184
2185             if (!el || !cacheItem) {
2186                 return false;
2187             }
2188
2189             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2190
2191             delete listeners[index][this.WFN];
2192             delete listeners[index][this.FN];
2193             listeners.splice(index, 1);
2194
2195             return true;
2196
2197         },
2198
2199
2200         getTarget: function(ev, resolveTextNode) {
2201             ev = ev.browserEvent || ev;
2202             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2203             var t = ev.target || ev.srcElement;
2204             return this.resolveTextNode(t);
2205         },
2206
2207
2208         resolveTextNode: function(node) {
2209             if (Roo.isSafari && node && 3 == node.nodeType) {
2210                 return node.parentNode;
2211             } else {
2212                 return node;
2213             }
2214         },
2215
2216
2217         getPageX: function(ev) {
2218             ev = ev.browserEvent || ev;
2219             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2220             var x = ev.pageX;
2221             if (!x && 0 !== x) {
2222                 x = ev.clientX || 0;
2223
2224                 if (Roo.isIE) {
2225                     x += this.getScroll()[1];
2226                 }
2227             }
2228
2229             return x;
2230         },
2231
2232
2233         getPageY: function(ev) {
2234             ev = ev.browserEvent || ev;
2235             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2236             var y = ev.pageY;
2237             if (!y && 0 !== y) {
2238                 y = ev.clientY || 0;
2239
2240                 if (Roo.isIE) {
2241                     y += this.getScroll()[0];
2242                 }
2243             }
2244
2245
2246             return y;
2247         },
2248
2249
2250         getXY: function(ev) {
2251             ev = ev.browserEvent || ev;
2252             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2253             return [this.getPageX(ev), this.getPageY(ev)];
2254         },
2255
2256
2257         getRelatedTarget: function(ev) {
2258             ev = ev.browserEvent || ev;
2259             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2260             var t = ev.relatedTarget;
2261             if (!t) {
2262                 if (ev.type == "mouseout") {
2263                     t = ev.toElement;
2264                 } else if (ev.type == "mouseover") {
2265                     t = ev.fromElement;
2266                 }
2267             }
2268
2269             return this.resolveTextNode(t);
2270         },
2271
2272
2273         getTime: function(ev) {
2274             ev = ev.browserEvent || ev;
2275             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2276             if (!ev.time) {
2277                 var t = new Date().getTime();
2278                 try {
2279                     ev.time = t;
2280                 } catch(ex) {
2281                     this.lastError = ex;
2282                     return t;
2283                 }
2284             }
2285
2286             return ev.time;
2287         },
2288
2289
2290         stopEvent: function(ev) {
2291             this.stopPropagation(ev);
2292             this.preventDefault(ev);
2293         },
2294
2295
2296         stopPropagation: function(ev) {
2297             ev = ev.browserEvent || ev;
2298             if (ev.stopPropagation) {
2299                 ev.stopPropagation();
2300             } else {
2301                 ev.cancelBubble = true;
2302             }
2303         },
2304
2305
2306         preventDefault: function(ev) {
2307             ev = ev.browserEvent || ev;
2308             if(ev.preventDefault) {
2309                 ev.preventDefault();
2310             } else {
2311                 ev.returnValue = false;
2312             }
2313         },
2314
2315
2316         getEvent: function(e) {
2317             var ev = e || window.event;
2318             if (!ev) {
2319                 var c = this.getEvent.caller;
2320                 while (c) {
2321                     ev = c.arguments[0];
2322                     if (ev && Event == ev.constructor) {
2323                         break;
2324                     }
2325                     c = c.caller;
2326                 }
2327             }
2328             return ev;
2329         },
2330
2331
2332         getCharCode: function(ev) {
2333             ev = ev.browserEvent || ev;
2334             return ev.charCode || ev.keyCode || 0;
2335         },
2336
2337
2338         _getCacheIndex: function(el, eventName, fn) {
2339             for (var i = 0,len = listeners.length; i < len; ++i) {
2340                 var li = listeners[i];
2341                 if (li &&
2342                     li[this.FN] == fn &&
2343                     li[this.EL] == el &&
2344                     li[this.TYPE] == eventName) {
2345                     return i;
2346                 }
2347             }
2348
2349             return -1;
2350         },
2351
2352
2353         elCache: {},
2354
2355
2356         getEl: function(id) {
2357             return document.getElementById(id);
2358         },
2359
2360
2361         clearCache: function() {
2362         },
2363
2364
2365         _load: function(e) {
2366             loadComplete = true;
2367             var EU = Roo.lib.Event;
2368
2369
2370             if (Roo.isIE) {
2371                 EU.doRemove(window, "load", EU._load);
2372             }
2373         },
2374
2375
2376         _tryPreloadAttach: function() {
2377
2378             if (this.locked) {
2379                 return false;
2380             }
2381
2382             this.locked = true;
2383
2384
2385             var tryAgain = !loadComplete;
2386             if (!tryAgain) {
2387                 tryAgain = (retryCount > 0);
2388             }
2389
2390
2391             var notAvail = [];
2392             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2393                 var item = onAvailStack[i];
2394                 if (item) {
2395                     var el = this.getEl(item.id);
2396
2397                     if (el) {
2398                         if (!item.checkReady ||
2399                             loadComplete ||
2400                             el.nextSibling ||
2401                             (document && document.body)) {
2402
2403                             var scope = el;
2404                             if (item.override) {
2405                                 if (item.override === true) {
2406                                     scope = item.obj;
2407                                 } else {
2408                                     scope = item.override;
2409                                 }
2410                             }
2411                             item.fn.call(scope, item.obj);
2412                             onAvailStack[i] = null;
2413                         }
2414                     } else {
2415                         notAvail.push(item);
2416                     }
2417                 }
2418             }
2419
2420             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2421
2422             if (tryAgain) {
2423
2424                 this.startInterval();
2425             } else {
2426                 clearInterval(this._interval);
2427                 this._interval = null;
2428             }
2429
2430             this.locked = false;
2431
2432             return true;
2433
2434         },
2435
2436
2437         purgeElement: function(el, recurse, eventName) {
2438             var elListeners = this.getListeners(el, eventName);
2439             if (elListeners) {
2440                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2441                     var l = elListeners[i];
2442                     this.removeListener(el, l.type, l.fn);
2443                 }
2444             }
2445
2446             if (recurse && el && el.childNodes) {
2447                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2448                     this.purgeElement(el.childNodes[i], recurse, eventName);
2449                 }
2450             }
2451         },
2452
2453
2454         getListeners: function(el, eventName) {
2455             var results = [], searchLists;
2456             if (!eventName) {
2457                 searchLists = [listeners, unloadListeners];
2458             } else if (eventName == "unload") {
2459                 searchLists = [unloadListeners];
2460             } else {
2461                 searchLists = [listeners];
2462             }
2463
2464             for (var j = 0; j < searchLists.length; ++j) {
2465                 var searchList = searchLists[j];
2466                 if (searchList && searchList.length > 0) {
2467                     for (var i = 0,len = searchList.length; i < len; ++i) {
2468                         var l = searchList[i];
2469                         if (l && l[this.EL] === el &&
2470                             (!eventName || eventName === l[this.TYPE])) {
2471                             results.push({
2472                                 type:   l[this.TYPE],
2473                                 fn:     l[this.FN],
2474                                 obj:    l[this.OBJ],
2475                                 adjust: l[this.ADJ_SCOPE],
2476                                 index:  i
2477                             });
2478                         }
2479                     }
2480                 }
2481             }
2482
2483             return (results.length) ? results : null;
2484         },
2485
2486
2487         _unload: function(e) {
2488
2489             var EU = Roo.lib.Event, i, j, l, len, index;
2490
2491             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2492                 l = unloadListeners[i];
2493                 if (l) {
2494                     var scope = window;
2495                     if (l[EU.ADJ_SCOPE]) {
2496                         if (l[EU.ADJ_SCOPE] === true) {
2497                             scope = l[EU.OBJ];
2498                         } else {
2499                             scope = l[EU.ADJ_SCOPE];
2500                         }
2501                     }
2502                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2503                     unloadListeners[i] = null;
2504                     l = null;
2505                     scope = null;
2506                 }
2507             }
2508
2509             unloadListeners = null;
2510
2511             if (listeners && listeners.length > 0) {
2512                 j = listeners.length;
2513                 while (j) {
2514                     index = j - 1;
2515                     l = listeners[index];
2516                     if (l) {
2517                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2518                                 l[EU.FN], index);
2519                     }
2520                     j = j - 1;
2521                 }
2522                 l = null;
2523
2524                 EU.clearCache();
2525             }
2526
2527             EU.doRemove(window, "unload", EU._unload);
2528
2529         },
2530
2531
2532         getScroll: function() {
2533             var dd = document.documentElement, db = document.body;
2534             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2535                 return [dd.scrollTop, dd.scrollLeft];
2536             } else if (db) {
2537                 return [db.scrollTop, db.scrollLeft];
2538             } else {
2539                 return [0, 0];
2540             }
2541         },
2542
2543
2544         doAdd: function () {
2545             if (window.addEventListener) {
2546                 return function(el, eventName, fn, capture) {
2547                     el.addEventListener(eventName, fn, (capture));
2548                 };
2549             } else if (window.attachEvent) {
2550                 return function(el, eventName, fn, capture) {
2551                     el.attachEvent("on" + eventName, fn);
2552                 };
2553             } else {
2554                 return function() {
2555                 };
2556             }
2557         }(),
2558
2559
2560         doRemove: function() {
2561             if (window.removeEventListener) {
2562                 return function (el, eventName, fn, capture) {
2563                     el.removeEventListener(eventName, fn, (capture));
2564                 };
2565             } else if (window.detachEvent) {
2566                 return function (el, eventName, fn) {
2567                     el.detachEvent("on" + eventName, fn);
2568                 };
2569             } else {
2570                 return function() {
2571                 };
2572             }
2573         }()
2574     };
2575     
2576 }();
2577 (function() {     
2578    
2579     var E = Roo.lib.Event;
2580     E.on = E.addListener;
2581     E.un = E.removeListener;
2582
2583     if (document && document.body) {
2584         E._load();
2585     } else {
2586         E.doAdd(window, "load", E._load);
2587     }
2588     E.doAdd(window, "unload", E._unload);
2589     E._tryPreloadAttach();
2590 })();
2591
2592 /*
2593  * Portions of this file are based on pieces of Yahoo User Interface Library
2594  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2595  * YUI licensed under the BSD License:
2596  * http://developer.yahoo.net/yui/license.txt
2597  * <script type="text/javascript">
2598  *
2599  */
2600
2601 (function() {
2602     /**
2603      * @class Roo.lib.Ajax
2604      *
2605      */
2606     Roo.lib.Ajax = {
2607         /**
2608          * @static 
2609          */
2610         request : function(method, uri, cb, data, options) {
2611             if(options){
2612                 var hs = options.headers;
2613                 if(hs){
2614                     for(var h in hs){
2615                         if(hs.hasOwnProperty(h)){
2616                             this.initHeader(h, hs[h], false);
2617                         }
2618                     }
2619                 }
2620                 if(options.xmlData){
2621                     this.initHeader('Content-Type', 'text/xml', false);
2622                     method = 'POST';
2623                     data = options.xmlData;
2624                 }
2625             }
2626
2627             return this.asyncRequest(method, uri, cb, data);
2628         },
2629
2630         serializeForm : function(form) {
2631             if(typeof form == 'string') {
2632                 form = (document.getElementById(form) || document.forms[form]);
2633             }
2634
2635             var el, name, val, disabled, data = '', hasSubmit = false;
2636             for (var i = 0; i < form.elements.length; i++) {
2637                 el = form.elements[i];
2638                 disabled = form.elements[i].disabled;
2639                 name = form.elements[i].name;
2640                 val = form.elements[i].value;
2641
2642                 if (!disabled && name){
2643                     switch (el.type)
2644                             {
2645                         case 'select-one':
2646                         case 'select-multiple':
2647                             for (var j = 0; j < el.options.length; j++) {
2648                                 if (el.options[j].selected) {
2649                                     if (Roo.isIE) {
2650                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2651                                     }
2652                                     else {
2653                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2654                                     }
2655                                 }
2656                             }
2657                             break;
2658                         case 'radio':
2659                         case 'checkbox':
2660                             if (el.checked) {
2661                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2662                             }
2663                             break;
2664                         case 'file':
2665
2666                         case undefined:
2667
2668                         case 'reset':
2669
2670                         case 'button':
2671
2672                             break;
2673                         case 'submit':
2674                             if(hasSubmit == false) {
2675                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2676                                 hasSubmit = true;
2677                             }
2678                             break;
2679                         default:
2680                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2681                             break;
2682                     }
2683                 }
2684             }
2685             data = data.substr(0, data.length - 1);
2686             return data;
2687         },
2688
2689         headers:{},
2690
2691         hasHeaders:false,
2692
2693         useDefaultHeader:true,
2694
2695         defaultPostHeader:'application/x-www-form-urlencoded',
2696
2697         useDefaultXhrHeader:true,
2698
2699         defaultXhrHeader:'XMLHttpRequest',
2700
2701         hasDefaultHeaders:true,
2702
2703         defaultHeaders:{},
2704
2705         poll:{},
2706
2707         timeout:{},
2708
2709         pollInterval:50,
2710
2711         transactionId:0,
2712
2713         setProgId:function(id)
2714         {
2715             this.activeX.unshift(id);
2716         },
2717
2718         setDefaultPostHeader:function(b)
2719         {
2720             this.useDefaultHeader = b;
2721         },
2722
2723         setDefaultXhrHeader:function(b)
2724         {
2725             this.useDefaultXhrHeader = b;
2726         },
2727
2728         setPollingInterval:function(i)
2729         {
2730             if (typeof i == 'number' && isFinite(i)) {
2731                 this.pollInterval = i;
2732             }
2733         },
2734
2735         createXhrObject:function(transactionId)
2736         {
2737             var obj,http;
2738             try
2739             {
2740
2741                 http = new XMLHttpRequest();
2742
2743                 obj = { conn:http, tId:transactionId };
2744             }
2745             catch(e)
2746             {
2747                 for (var i = 0; i < this.activeX.length; ++i) {
2748                     try
2749                     {
2750
2751                         http = new ActiveXObject(this.activeX[i]);
2752
2753                         obj = { conn:http, tId:transactionId };
2754                         break;
2755                     }
2756                     catch(e) {
2757                     }
2758                 }
2759             }
2760             finally
2761             {
2762                 return obj;
2763             }
2764         },
2765
2766         getConnectionObject:function()
2767         {
2768             var o;
2769             var tId = this.transactionId;
2770
2771             try
2772             {
2773                 o = this.createXhrObject(tId);
2774                 if (o) {
2775                     this.transactionId++;
2776                 }
2777             }
2778             catch(e) {
2779             }
2780             finally
2781             {
2782                 return o;
2783             }
2784         },
2785
2786         asyncRequest:function(method, uri, callback, postData)
2787         {
2788             var o = this.getConnectionObject();
2789
2790             if (!o) {
2791                 return null;
2792             }
2793             else {
2794                 o.conn.open(method, uri, true);
2795
2796                 if (this.useDefaultXhrHeader) {
2797                     if (!this.defaultHeaders['X-Requested-With']) {
2798                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2799                     }
2800                 }
2801
2802                 if(postData && this.useDefaultHeader){
2803                     this.initHeader('Content-Type', this.defaultPostHeader);
2804                 }
2805
2806                  if (this.hasDefaultHeaders || this.hasHeaders) {
2807                     this.setHeader(o);
2808                 }
2809
2810                 this.handleReadyState(o, callback);
2811                 o.conn.send(postData || null);
2812
2813                 return o;
2814             }
2815         },
2816
2817         handleReadyState:function(o, callback)
2818         {
2819             var oConn = this;
2820
2821             if (callback && callback.timeout) {
2822                 
2823                 this.timeout[o.tId] = window.setTimeout(function() {
2824                     oConn.abort(o, callback, true);
2825                 }, callback.timeout);
2826             }
2827
2828             this.poll[o.tId] = window.setInterval(
2829                     function() {
2830                         if (o.conn && o.conn.readyState == 4) {
2831                             window.clearInterval(oConn.poll[o.tId]);
2832                             delete oConn.poll[o.tId];
2833
2834                             if(callback && callback.timeout) {
2835                                 window.clearTimeout(oConn.timeout[o.tId]);
2836                                 delete oConn.timeout[o.tId];
2837                             }
2838
2839                             oConn.handleTransactionResponse(o, callback);
2840                         }
2841                     }
2842                     , this.pollInterval);
2843         },
2844
2845         handleTransactionResponse:function(o, callback, isAbort)
2846         {
2847
2848             if (!callback) {
2849                 this.releaseObject(o);
2850                 return;
2851             }
2852
2853             var httpStatus, responseObject;
2854
2855             try
2856             {
2857                 if (o.conn.status !== undefined && o.conn.status != 0) {
2858                     httpStatus = o.conn.status;
2859                 }
2860                 else {
2861                     httpStatus = 13030;
2862                 }
2863             }
2864             catch(e) {
2865
2866
2867                 httpStatus = 13030;
2868             }
2869
2870             if (httpStatus >= 200 && httpStatus < 300) {
2871                 responseObject = this.createResponseObject(o, callback.argument);
2872                 if (callback.success) {
2873                     if (!callback.scope) {
2874                         callback.success(responseObject);
2875                     }
2876                     else {
2877
2878
2879                         callback.success.apply(callback.scope, [responseObject]);
2880                     }
2881                 }
2882             }
2883             else {
2884                 switch (httpStatus) {
2885
2886                     case 12002:
2887                     case 12029:
2888                     case 12030:
2889                     case 12031:
2890                     case 12152:
2891                     case 13030:
2892                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                         break;
2902                     default:
2903                         responseObject = this.createResponseObject(o, callback.argument);
2904                         if (callback.failure) {
2905                             if (!callback.scope) {
2906                                 callback.failure(responseObject);
2907                             }
2908                             else {
2909                                 callback.failure.apply(callback.scope, [responseObject]);
2910                             }
2911                         }
2912                 }
2913             }
2914
2915             this.releaseObject(o);
2916             responseObject = null;
2917         },
2918
2919         createResponseObject:function(o, callbackArg)
2920         {
2921             var obj = {};
2922             var headerObj = {};
2923
2924             try
2925             {
2926                 var headerStr = o.conn.getAllResponseHeaders();
2927                 var header = headerStr.split('\n');
2928                 for (var i = 0; i < header.length; i++) {
2929                     var delimitPos = header[i].indexOf(':');
2930                     if (delimitPos != -1) {
2931                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2932                     }
2933                 }
2934             }
2935             catch(e) {
2936             }
2937
2938             obj.tId = o.tId;
2939             obj.status = o.conn.status;
2940             obj.statusText = o.conn.statusText;
2941             obj.getResponseHeader = headerObj;
2942             obj.getAllResponseHeaders = headerStr;
2943             obj.responseText = o.conn.responseText;
2944             obj.responseXML = o.conn.responseXML;
2945
2946             if (typeof callbackArg !== undefined) {
2947                 obj.argument = callbackArg;
2948             }
2949
2950             return obj;
2951         },
2952
2953         createExceptionObject:function(tId, callbackArg, isAbort)
2954         {
2955             var COMM_CODE = 0;
2956             var COMM_ERROR = 'communication failure';
2957             var ABORT_CODE = -1;
2958             var ABORT_ERROR = 'transaction aborted';
2959
2960             var obj = {};
2961
2962             obj.tId = tId;
2963             if (isAbort) {
2964                 obj.status = ABORT_CODE;
2965                 obj.statusText = ABORT_ERROR;
2966             }
2967             else {
2968                 obj.status = COMM_CODE;
2969                 obj.statusText = COMM_ERROR;
2970             }
2971
2972             if (callbackArg) {
2973                 obj.argument = callbackArg;
2974             }
2975
2976             return obj;
2977         },
2978
2979         initHeader:function(label, value, isDefault)
2980         {
2981             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2982
2983             if (headerObj[label] === undefined) {
2984                 headerObj[label] = value;
2985             }
2986             else {
2987
2988
2989                 headerObj[label] = value + "," + headerObj[label];
2990             }
2991
2992             if (isDefault) {
2993                 this.hasDefaultHeaders = true;
2994             }
2995             else {
2996                 this.hasHeaders = true;
2997             }
2998         },
2999
3000
3001         setHeader:function(o)
3002         {
3003             if (this.hasDefaultHeaders) {
3004                 for (var prop in this.defaultHeaders) {
3005                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3006                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3007                     }
3008                 }
3009             }
3010
3011             if (this.hasHeaders) {
3012                 for (var prop in this.headers) {
3013                     if (this.headers.hasOwnProperty(prop)) {
3014                         o.conn.setRequestHeader(prop, this.headers[prop]);
3015                     }
3016                 }
3017                 this.headers = {};
3018                 this.hasHeaders = false;
3019             }
3020         },
3021
3022         resetDefaultHeaders:function() {
3023             delete this.defaultHeaders;
3024             this.defaultHeaders = {};
3025             this.hasDefaultHeaders = false;
3026         },
3027
3028         abort:function(o, callback, isTimeout)
3029         {
3030             if(this.isCallInProgress(o)) {
3031                 o.conn.abort();
3032                 window.clearInterval(this.poll[o.tId]);
3033                 delete this.poll[o.tId];
3034                 if (isTimeout) {
3035                     delete this.timeout[o.tId];
3036                 }
3037
3038                 this.handleTransactionResponse(o, callback, true);
3039
3040                 return true;
3041             }
3042             else {
3043                 return false;
3044             }
3045         },
3046
3047
3048         isCallInProgress:function(o)
3049         {
3050             if (o && o.conn) {
3051                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3052             }
3053             else {
3054
3055                 return false;
3056             }
3057         },
3058
3059
3060         releaseObject:function(o)
3061         {
3062
3063             o.conn = null;
3064
3065             o = null;
3066         },
3067
3068         activeX:[
3069         'MSXML2.XMLHTTP.3.0',
3070         'MSXML2.XMLHTTP',
3071         'Microsoft.XMLHTTP'
3072         ]
3073
3074
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 Roo.lib.Region = function(t, r, b, l) {
3086     this.top = t;
3087     this[1] = t;
3088     this.right = r;
3089     this.bottom = b;
3090     this.left = l;
3091     this[0] = l;
3092 };
3093
3094
3095 Roo.lib.Region.prototype = {
3096     contains : function(region) {
3097         return ( region.left >= this.left &&
3098                  region.right <= this.right &&
3099                  region.top >= this.top &&
3100                  region.bottom <= this.bottom    );
3101
3102     },
3103
3104     getArea : function() {
3105         return ( (this.bottom - this.top) * (this.right - this.left) );
3106     },
3107
3108     intersect : function(region) {
3109         var t = Math.max(this.top, region.top);
3110         var r = Math.min(this.right, region.right);
3111         var b = Math.min(this.bottom, region.bottom);
3112         var l = Math.max(this.left, region.left);
3113
3114         if (b >= t && r >= l) {
3115             return new Roo.lib.Region(t, r, b, l);
3116         } else {
3117             return null;
3118         }
3119     },
3120     union : function(region) {
3121         var t = Math.min(this.top, region.top);
3122         var r = Math.max(this.right, region.right);
3123         var b = Math.max(this.bottom, region.bottom);
3124         var l = Math.min(this.left, region.left);
3125
3126         return new Roo.lib.Region(t, r, b, l);
3127     },
3128
3129     adjust : function(t, l, b, r) {
3130         this.top += t;
3131         this.left += l;
3132         this.right += r;
3133         this.bottom += b;
3134         return this;
3135     }
3136 };
3137
3138 Roo.lib.Region.getRegion = function(el) {
3139     var p = Roo.lib.Dom.getXY(el);
3140
3141     var t = p[1];
3142     var r = p[0] + el.offsetWidth;
3143     var b = p[1] + el.offsetHeight;
3144     var l = p[0];
3145
3146     return new Roo.lib.Region(t, r, b, l);
3147 };
3148 /*
3149  * Portions of this file are based on pieces of Yahoo User Interface Library
3150  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3151  * YUI licensed under the BSD License:
3152  * http://developer.yahoo.net/yui/license.txt
3153  * <script type="text/javascript">
3154  *
3155  */
3156 //@@dep Roo.lib.Region
3157
3158
3159 Roo.lib.Point = function(x, y) {
3160     if (x instanceof Array) {
3161         y = x[1];
3162         x = x[0];
3163     }
3164     this.x = this.right = this.left = this[0] = x;
3165     this.y = this.top = this.bottom = this[1] = y;
3166 };
3167
3168 Roo.lib.Point.prototype = new Roo.lib.Region();
3169 /*
3170  * Portions of this file are based on pieces of Yahoo User Interface Library
3171  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3172  * YUI licensed under the BSD License:
3173  * http://developer.yahoo.net/yui/license.txt
3174  * <script type="text/javascript">
3175  *
3176  */
3177  
3178 (function() {   
3179
3180     Roo.lib.Anim = {
3181         scroll : function(el, args, duration, easing, cb, scope) {
3182             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3183         },
3184
3185         motion : function(el, args, duration, easing, cb, scope) {
3186             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3187         },
3188
3189         color : function(el, args, duration, easing, cb, scope) {
3190             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3191         },
3192
3193         run : function(el, args, duration, easing, cb, scope, type) {
3194             type = type || Roo.lib.AnimBase;
3195             if (typeof easing == "string") {
3196                 easing = Roo.lib.Easing[easing];
3197             }
3198             var anim = new type(el, args, duration, easing);
3199             anim.animateX(function() {
3200                 Roo.callback(cb, scope);
3201             });
3202             return anim;
3203         }
3204     };
3205 })();/*
3206  * Portions of this file are based on pieces of Yahoo User Interface Library
3207  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3208  * YUI licensed under the BSD License:
3209  * http://developer.yahoo.net/yui/license.txt
3210  * <script type="text/javascript">
3211  *
3212  */
3213
3214 (function() {    
3215     var libFlyweight;
3216     
3217     function fly(el) {
3218         if (!libFlyweight) {
3219             libFlyweight = new Roo.Element.Flyweight();
3220         }
3221         libFlyweight.dom = el;
3222         return libFlyweight;
3223     }
3224
3225     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3226     
3227    
3228     
3229     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3230         if (el) {
3231             this.init(el, attributes, duration, method);
3232         }
3233     };
3234
3235     Roo.lib.AnimBase.fly = fly;
3236     
3237     
3238     
3239     Roo.lib.AnimBase.prototype = {
3240
3241         toString: function() {
3242             var el = this.getEl();
3243             var id = el.id || el.tagName;
3244             return ("Anim " + id);
3245         },
3246
3247         patterns: {
3248             noNegatives:        /width|height|opacity|padding/i,
3249             offsetAttribute:  /^((width|height)|(top|left))$/,
3250             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3251             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3252         },
3253
3254
3255         doMethod: function(attr, start, end) {
3256             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3257         },
3258
3259
3260         setAttribute: function(attr, val, unit) {
3261             if (this.patterns.noNegatives.test(attr)) {
3262                 val = (val > 0) ? val : 0;
3263             }
3264
3265             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3266         },
3267
3268
3269         getAttribute: function(attr) {
3270             var el = this.getEl();
3271             var val = fly(el).getStyle(attr);
3272
3273             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3274                 return parseFloat(val);
3275             }
3276
3277             var a = this.patterns.offsetAttribute.exec(attr) || [];
3278             var pos = !!( a[3] );
3279             var box = !!( a[2] );
3280
3281
3282             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3283                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3284             } else {
3285                 val = 0;
3286             }
3287
3288             return val;
3289         },
3290
3291
3292         getDefaultUnit: function(attr) {
3293             if (this.patterns.defaultUnit.test(attr)) {
3294                 return 'px';
3295             }
3296
3297             return '';
3298         },
3299
3300         animateX : function(callback, scope) {
3301             var f = function() {
3302                 this.onComplete.removeListener(f);
3303                 if (typeof callback == "function") {
3304                     callback.call(scope || this, this);
3305                 }
3306             };
3307             this.onComplete.addListener(f, this);
3308             this.animate();
3309         },
3310
3311
3312         setRuntimeAttribute: function(attr) {
3313             var start;
3314             var end;
3315             var attributes = this.attributes;
3316
3317             this.runtimeAttributes[attr] = {};
3318
3319             var isset = function(prop) {
3320                 return (typeof prop !== 'undefined');
3321             };
3322
3323             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3324                 return false;
3325             }
3326
3327             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3328
3329
3330             if (isset(attributes[attr]['to'])) {
3331                 end = attributes[attr]['to'];
3332             } else if (isset(attributes[attr]['by'])) {
3333                 if (start.constructor == Array) {
3334                     end = [];
3335                     for (var i = 0, len = start.length; i < len; ++i) {
3336                         end[i] = start[i] + attributes[attr]['by'][i];
3337                     }
3338                 } else {
3339                     end = start + attributes[attr]['by'];
3340                 }
3341             }
3342
3343             this.runtimeAttributes[attr].start = start;
3344             this.runtimeAttributes[attr].end = end;
3345
3346
3347             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3348         },
3349
3350
3351         init: function(el, attributes, duration, method) {
3352
3353             var isAnimated = false;
3354
3355
3356             var startTime = null;
3357
3358
3359             var actualFrames = 0;
3360
3361
3362             el = Roo.getDom(el);
3363
3364
3365             this.attributes = attributes || {};
3366
3367
3368             this.duration = duration || 1;
3369
3370
3371             this.method = method || Roo.lib.Easing.easeNone;
3372
3373
3374             this.useSeconds = true;
3375
3376
3377             this.currentFrame = 0;
3378
3379
3380             this.totalFrames = Roo.lib.AnimMgr.fps;
3381
3382
3383             this.getEl = function() {
3384                 return el;
3385             };
3386
3387
3388             this.isAnimated = function() {
3389                 return isAnimated;
3390             };
3391
3392
3393             this.getStartTime = function() {
3394                 return startTime;
3395             };
3396
3397             this.runtimeAttributes = {};
3398
3399
3400             this.animate = function() {
3401                 if (this.isAnimated()) {
3402                     return false;
3403                 }
3404
3405                 this.currentFrame = 0;
3406
3407                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3408
3409                 Roo.lib.AnimMgr.registerElement(this);
3410             };
3411
3412
3413             this.stop = function(finish) {
3414                 if (finish) {
3415                     this.currentFrame = this.totalFrames;
3416                     this._onTween.fire();
3417                 }
3418                 Roo.lib.AnimMgr.stop(this);
3419             };
3420
3421             var onStart = function() {
3422                 this.onStart.fire();
3423
3424                 this.runtimeAttributes = {};
3425                 for (var attr in this.attributes) {
3426                     this.setRuntimeAttribute(attr);
3427                 }
3428
3429                 isAnimated = true;
3430                 actualFrames = 0;
3431                 startTime = new Date();
3432             };
3433
3434
3435             var onTween = function() {
3436                 var data = {
3437                     duration: new Date() - this.getStartTime(),
3438                     currentFrame: this.currentFrame
3439                 };
3440
3441                 data.toString = function() {
3442                     return (
3443                             'duration: ' + data.duration +
3444                             ', currentFrame: ' + data.currentFrame
3445                             );
3446                 };
3447
3448                 this.onTween.fire(data);
3449
3450                 var runtimeAttributes = this.runtimeAttributes;
3451
3452                 for (var attr in runtimeAttributes) {
3453                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3454                 }
3455
3456                 actualFrames += 1;
3457             };
3458
3459             var onComplete = function() {
3460                 var actual_duration = (new Date() - startTime) / 1000 ;
3461
3462                 var data = {
3463                     duration: actual_duration,
3464                     frames: actualFrames,
3465                     fps: actualFrames / actual_duration
3466                 };
3467
3468                 data.toString = function() {
3469                     return (
3470                             'duration: ' + data.duration +
3471                             ', frames: ' + data.frames +
3472                             ', fps: ' + data.fps
3473                             );
3474                 };
3475
3476                 isAnimated = false;
3477                 actualFrames = 0;
3478                 this.onComplete.fire(data);
3479             };
3480
3481
3482             this._onStart = new Roo.util.Event(this);
3483             this.onStart = new Roo.util.Event(this);
3484             this.onTween = new Roo.util.Event(this);
3485             this._onTween = new Roo.util.Event(this);
3486             this.onComplete = new Roo.util.Event(this);
3487             this._onComplete = new Roo.util.Event(this);
3488             this._onStart.addListener(onStart);
3489             this._onTween.addListener(onTween);
3490             this._onComplete.addListener(onComplete);
3491         }
3492     };
3493 })();
3494 /*
3495  * Portions of this file are based on pieces of Yahoo User Interface Library
3496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3497  * YUI licensed under the BSD License:
3498  * http://developer.yahoo.net/yui/license.txt
3499  * <script type="text/javascript">
3500  *
3501  */
3502
3503 Roo.lib.AnimMgr = new function() {
3504
3505     var thread = null;
3506
3507
3508     var queue = [];
3509
3510
3511     var tweenCount = 0;
3512
3513
3514     this.fps = 1000;
3515
3516
3517     this.delay = 1;
3518
3519
3520     this.registerElement = function(tween) {
3521         queue[queue.length] = tween;
3522         tweenCount += 1;
3523         tween._onStart.fire();
3524         this.start();
3525     };
3526
3527
3528     this.unRegister = function(tween, index) {
3529         tween._onComplete.fire();
3530         index = index || getIndex(tween);
3531         if (index != -1) {
3532             queue.splice(index, 1);
3533         }
3534
3535         tweenCount -= 1;
3536         if (tweenCount <= 0) {
3537             this.stop();
3538         }
3539     };
3540
3541
3542     this.start = function() {
3543         if (thread === null) {
3544             thread = setInterval(this.run, this.delay);
3545         }
3546     };
3547
3548
3549     this.stop = function(tween) {
3550         if (!tween) {
3551             clearInterval(thread);
3552
3553             for (var i = 0, len = queue.length; i < len; ++i) {
3554                 if (queue[0].isAnimated()) {
3555                     this.unRegister(queue[0], 0);
3556                 }
3557             }
3558
3559             queue = [];
3560             thread = null;
3561             tweenCount = 0;
3562         }
3563         else {
3564             this.unRegister(tween);
3565         }
3566     };
3567
3568
3569     this.run = function() {
3570         for (var i = 0, len = queue.length; i < len; ++i) {
3571             var tween = queue[i];
3572             if (!tween || !tween.isAnimated()) {
3573                 continue;
3574             }
3575
3576             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3577             {
3578                 tween.currentFrame += 1;
3579
3580                 if (tween.useSeconds) {
3581                     correctFrame(tween);
3582                 }
3583                 tween._onTween.fire();
3584             }
3585             else {
3586                 Roo.lib.AnimMgr.stop(tween, i);
3587             }
3588         }
3589     };
3590
3591     var getIndex = function(anim) {
3592         for (var i = 0, len = queue.length; i < len; ++i) {
3593             if (queue[i] == anim) {
3594                 return i;
3595             }
3596         }
3597         return -1;
3598     };
3599
3600
3601     var correctFrame = function(tween) {
3602         var frames = tween.totalFrames;
3603         var frame = tween.currentFrame;
3604         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3605         var elapsed = (new Date() - tween.getStartTime());
3606         var tweak = 0;
3607
3608         if (elapsed < tween.duration * 1000) {
3609             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3610         } else {
3611             tweak = frames - (frame + 1);
3612         }
3613         if (tweak > 0 && isFinite(tweak)) {
3614             if (tween.currentFrame + tweak >= frames) {
3615                 tweak = frames - (frame + 1);
3616             }
3617
3618             tween.currentFrame += tweak;
3619         }
3620     };
3621 };
3622
3623     /*
3624  * Portions of this file are based on pieces of Yahoo User Interface Library
3625  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3626  * YUI licensed under the BSD License:
3627  * http://developer.yahoo.net/yui/license.txt
3628  * <script type="text/javascript">
3629  *
3630  */
3631 Roo.lib.Bezier = new function() {
3632
3633         this.getPosition = function(points, t) {
3634             var n = points.length;
3635             var tmp = [];
3636
3637             for (var i = 0; i < n; ++i) {
3638                 tmp[i] = [points[i][0], points[i][1]];
3639             }
3640
3641             for (var j = 1; j < n; ++j) {
3642                 for (i = 0; i < n - j; ++i) {
3643                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3644                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3645                 }
3646             }
3647
3648             return [ tmp[0][0], tmp[0][1] ];
3649
3650         };
3651     };/*
3652  * Portions of this file are based on pieces of Yahoo User Interface Library
3653  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3654  * YUI licensed under the BSD License:
3655  * http://developer.yahoo.net/yui/license.txt
3656  * <script type="text/javascript">
3657  *
3658  */
3659 (function() {
3660
3661     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3662         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3663     };
3664
3665     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3666
3667     var fly = Roo.lib.AnimBase.fly;
3668     var Y = Roo.lib;
3669     var superclass = Y.ColorAnim.superclass;
3670     var proto = Y.ColorAnim.prototype;
3671
3672     proto.toString = function() {
3673         var el = this.getEl();
3674         var id = el.id || el.tagName;
3675         return ("ColorAnim " + id);
3676     };
3677
3678     proto.patterns.color = /color$/i;
3679     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3680     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3681     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3682     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3683
3684
3685     proto.parseColor = function(s) {
3686         if (s.length == 3) {
3687             return s;
3688         }
3689
3690         var c = this.patterns.hex.exec(s);
3691         if (c && c.length == 4) {
3692             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3693         }
3694
3695         c = this.patterns.rgb.exec(s);
3696         if (c && c.length == 4) {
3697             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3698         }
3699
3700         c = this.patterns.hex3.exec(s);
3701         if (c && c.length == 4) {
3702             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3703         }
3704
3705         return null;
3706     };
3707     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3708     proto.getAttribute = function(attr) {
3709         var el = this.getEl();
3710         if (this.patterns.color.test(attr)) {
3711             var val = fly(el).getStyle(attr);
3712
3713             if (this.patterns.transparent.test(val)) {
3714                 var parent = el.parentNode;
3715                 val = fly(parent).getStyle(attr);
3716
3717                 while (parent && this.patterns.transparent.test(val)) {
3718                     parent = parent.parentNode;
3719                     val = fly(parent).getStyle(attr);
3720                     if (parent.tagName.toUpperCase() == 'HTML') {
3721                         val = '#fff';
3722                     }
3723                 }
3724             }
3725         } else {
3726             val = superclass.getAttribute.call(this, attr);
3727         }
3728
3729         return val;
3730     };
3731     proto.getAttribute = function(attr) {
3732         var el = this.getEl();
3733         if (this.patterns.color.test(attr)) {
3734             var val = fly(el).getStyle(attr);
3735
3736             if (this.patterns.transparent.test(val)) {
3737                 var parent = el.parentNode;
3738                 val = fly(parent).getStyle(attr);
3739
3740                 while (parent && this.patterns.transparent.test(val)) {
3741                     parent = parent.parentNode;
3742                     val = fly(parent).getStyle(attr);
3743                     if (parent.tagName.toUpperCase() == 'HTML') {
3744                         val = '#fff';
3745                     }
3746                 }
3747             }
3748         } else {
3749             val = superclass.getAttribute.call(this, attr);
3750         }
3751
3752         return val;
3753     };
3754
3755     proto.doMethod = function(attr, start, end) {
3756         var val;
3757
3758         if (this.patterns.color.test(attr)) {
3759             val = [];
3760             for (var i = 0, len = start.length; i < len; ++i) {
3761                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3762             }
3763
3764             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3765         }
3766         else {
3767             val = superclass.doMethod.call(this, attr, start, end);
3768         }
3769
3770         return val;
3771     };
3772
3773     proto.setRuntimeAttribute = function(attr) {
3774         superclass.setRuntimeAttribute.call(this, attr);
3775
3776         if (this.patterns.color.test(attr)) {
3777             var attributes = this.attributes;
3778             var start = this.parseColor(this.runtimeAttributes[attr].start);
3779             var end = this.parseColor(this.runtimeAttributes[attr].end);
3780
3781             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3782                 end = this.parseColor(attributes[attr].by);
3783
3784                 for (var i = 0, len = start.length; i < len; ++i) {
3785                     end[i] = start[i] + end[i];
3786                 }
3787             }
3788
3789             this.runtimeAttributes[attr].start = start;
3790             this.runtimeAttributes[attr].end = end;
3791         }
3792     };
3793 })();
3794
3795 /*
3796  * Portions of this file are based on pieces of Yahoo User Interface Library
3797  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3798  * YUI licensed under the BSD License:
3799  * http://developer.yahoo.net/yui/license.txt
3800  * <script type="text/javascript">
3801  *
3802  */
3803 Roo.lib.Easing = {
3804
3805
3806     easeNone: function (t, b, c, d) {
3807         return c * t / d + b;
3808     },
3809
3810
3811     easeIn: function (t, b, c, d) {
3812         return c * (t /= d) * t + b;
3813     },
3814
3815
3816     easeOut: function (t, b, c, d) {
3817         return -c * (t /= d) * (t - 2) + b;
3818     },
3819
3820
3821     easeBoth: function (t, b, c, d) {
3822         if ((t /= d / 2) < 1) {
3823             return c / 2 * t * t + b;
3824         }
3825
3826         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3827     },
3828
3829
3830     easeInStrong: function (t, b, c, d) {
3831         return c * (t /= d) * t * t * t + b;
3832     },
3833
3834
3835     easeOutStrong: function (t, b, c, d) {
3836         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3837     },
3838
3839
3840     easeBothStrong: function (t, b, c, d) {
3841         if ((t /= d / 2) < 1) {
3842             return c / 2 * t * t * t * t + b;
3843         }
3844
3845         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3846     },
3847
3848
3849
3850     elasticIn: function (t, b, c, d, a, p) {
3851         if (t == 0) {
3852             return b;
3853         }
3854         if ((t /= d) == 1) {
3855             return b + c;
3856         }
3857         if (!p) {
3858             p = d * .3;
3859         }
3860
3861         if (!a || a < Math.abs(c)) {
3862             a = c;
3863             var s = p / 4;
3864         }
3865         else {
3866             var s = p / (2 * Math.PI) * Math.asin(c / a);
3867         }
3868
3869         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3870     },
3871
3872
3873     elasticOut: function (t, b, c, d, a, p) {
3874         if (t == 0) {
3875             return b;
3876         }
3877         if ((t /= d) == 1) {
3878             return b + c;
3879         }
3880         if (!p) {
3881             p = d * .3;
3882         }
3883
3884         if (!a || a < Math.abs(c)) {
3885             a = c;
3886             var s = p / 4;
3887         }
3888         else {
3889             var s = p / (2 * Math.PI) * Math.asin(c / a);
3890         }
3891
3892         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3893     },
3894
3895
3896     elasticBoth: function (t, b, c, d, a, p) {
3897         if (t == 0) {
3898             return b;
3899         }
3900
3901         if ((t /= d / 2) == 2) {
3902             return b + c;
3903         }
3904
3905         if (!p) {
3906             p = d * (.3 * 1.5);
3907         }
3908
3909         if (!a || a < Math.abs(c)) {
3910             a = c;
3911             var s = p / 4;
3912         }
3913         else {
3914             var s = p / (2 * Math.PI) * Math.asin(c / a);
3915         }
3916
3917         if (t < 1) {
3918             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3919                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3920         }
3921         return a * Math.pow(2, -10 * (t -= 1)) *
3922                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3923     },
3924
3925
3926
3927     backIn: function (t, b, c, d, s) {
3928         if (typeof s == 'undefined') {
3929             s = 1.70158;
3930         }
3931         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3932     },
3933
3934
3935     backOut: function (t, b, c, d, s) {
3936         if (typeof s == 'undefined') {
3937             s = 1.70158;
3938         }
3939         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3940     },
3941
3942
3943     backBoth: function (t, b, c, d, s) {
3944         if (typeof s == 'undefined') {
3945             s = 1.70158;
3946         }
3947
3948         if ((t /= d / 2 ) < 1) {
3949             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3950         }
3951         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3952     },
3953
3954
3955     bounceIn: function (t, b, c, d) {
3956         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3957     },
3958
3959
3960     bounceOut: function (t, b, c, d) {
3961         if ((t /= d) < (1 / 2.75)) {
3962             return c * (7.5625 * t * t) + b;
3963         } else if (t < (2 / 2.75)) {
3964             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3965         } else if (t < (2.5 / 2.75)) {
3966             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3967         }
3968         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3969     },
3970
3971
3972     bounceBoth: function (t, b, c, d) {
3973         if (t < d / 2) {
3974             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3975         }
3976         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3977     }
3978 };/*
3979  * Portions of this file are based on pieces of Yahoo User Interface Library
3980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3981  * YUI licensed under the BSD License:
3982  * http://developer.yahoo.net/yui/license.txt
3983  * <script type="text/javascript">
3984  *
3985  */
3986     (function() {
3987         Roo.lib.Motion = function(el, attributes, duration, method) {
3988             if (el) {
3989                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3990             }
3991         };
3992
3993         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3994
3995
3996         var Y = Roo.lib;
3997         var superclass = Y.Motion.superclass;
3998         var proto = Y.Motion.prototype;
3999
4000         proto.toString = function() {
4001             var el = this.getEl();
4002             var id = el.id || el.tagName;
4003             return ("Motion " + id);
4004         };
4005
4006         proto.patterns.points = /^points$/i;
4007
4008         proto.setAttribute = function(attr, val, unit) {
4009             if (this.patterns.points.test(attr)) {
4010                 unit = unit || 'px';
4011                 superclass.setAttribute.call(this, 'left', val[0], unit);
4012                 superclass.setAttribute.call(this, 'top', val[1], unit);
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017
4018         proto.getAttribute = function(attr) {
4019             if (this.patterns.points.test(attr)) {
4020                 var val = [
4021                         superclass.getAttribute.call(this, 'left'),
4022                         superclass.getAttribute.call(this, 'top')
4023                         ];
4024             } else {
4025                 val = superclass.getAttribute.call(this, attr);
4026             }
4027
4028             return val;
4029         };
4030
4031         proto.doMethod = function(attr, start, end) {
4032             var val = null;
4033
4034             if (this.patterns.points.test(attr)) {
4035                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4036                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4037             } else {
4038                 val = superclass.doMethod.call(this, attr, start, end);
4039             }
4040             return val;
4041         };
4042
4043         proto.setRuntimeAttribute = function(attr) {
4044             if (this.patterns.points.test(attr)) {
4045                 var el = this.getEl();
4046                 var attributes = this.attributes;
4047                 var start;
4048                 var control = attributes['points']['control'] || [];
4049                 var end;
4050                 var i, len;
4051
4052                 if (control.length > 0 && !(control[0] instanceof Array)) {
4053                     control = [control];
4054                 } else {
4055                     var tmp = [];
4056                     for (i = 0,len = control.length; i < len; ++i) {
4057                         tmp[i] = control[i];
4058                     }
4059                     control = tmp;
4060                 }
4061
4062                 Roo.fly(el).position();
4063
4064                 if (isset(attributes['points']['from'])) {
4065                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4066                 }
4067                 else {
4068                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4069                 }
4070
4071                 start = this.getAttribute('points');
4072
4073
4074                 if (isset(attributes['points']['to'])) {
4075                     end = translateValues.call(this, attributes['points']['to'], start);
4076
4077                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4078                     for (i = 0,len = control.length; i < len; ++i) {
4079                         control[i] = translateValues.call(this, control[i], start);
4080                     }
4081
4082
4083                 } else if (isset(attributes['points']['by'])) {
4084                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4085
4086                     for (i = 0,len = control.length; i < len; ++i) {
4087                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4088                     }
4089                 }
4090
4091                 this.runtimeAttributes[attr] = [start];
4092
4093                 if (control.length > 0) {
4094                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4095                 }
4096
4097                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4098             }
4099             else {
4100                 superclass.setRuntimeAttribute.call(this, attr);
4101             }
4102         };
4103
4104         var translateValues = function(val, start) {
4105             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4106             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4107
4108             return val;
4109         };
4110
4111         var isset = function(prop) {
4112             return (typeof prop !== 'undefined');
4113         };
4114     })();
4115 /*
4116  * Portions of this file are based on pieces of Yahoo User Interface Library
4117  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4118  * YUI licensed under the BSD License:
4119  * http://developer.yahoo.net/yui/license.txt
4120  * <script type="text/javascript">
4121  *
4122  */
4123     (function() {
4124         Roo.lib.Scroll = function(el, attributes, duration, method) {
4125             if (el) {
4126                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4127             }
4128         };
4129
4130         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4131
4132
4133         var Y = Roo.lib;
4134         var superclass = Y.Scroll.superclass;
4135         var proto = Y.Scroll.prototype;
4136
4137         proto.toString = function() {
4138             var el = this.getEl();
4139             var id = el.id || el.tagName;
4140             return ("Scroll " + id);
4141         };
4142
4143         proto.doMethod = function(attr, start, end) {
4144             var val = null;
4145
4146             if (attr == 'scroll') {
4147                 val = [
4148                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4149                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4150                         ];
4151
4152             } else {
4153                 val = superclass.doMethod.call(this, attr, start, end);
4154             }
4155             return val;
4156         };
4157
4158         proto.getAttribute = function(attr) {
4159             var val = null;
4160             var el = this.getEl();
4161
4162             if (attr == 'scroll') {
4163                 val = [ el.scrollLeft, el.scrollTop ];
4164             } else {
4165                 val = superclass.getAttribute.call(this, attr);
4166             }
4167
4168             return val;
4169         };
4170
4171         proto.setAttribute = function(attr, val, unit) {
4172             var el = this.getEl();
4173
4174             if (attr == 'scroll') {
4175                 el.scrollLeft = val[0];
4176                 el.scrollTop = val[1];
4177             } else {
4178                 superclass.setAttribute.call(this, attr, val, unit);
4179             }
4180         };
4181     })();
4182 /*
4183  * Based on:
4184  * Ext JS Library 1.1.1
4185  * Copyright(c) 2006-2007, Ext JS, LLC.
4186  *
4187  * Originally Released Under LGPL - original licence link has changed is not relivant.
4188  *
4189  * Fork - LGPL
4190  * <script type="text/javascript">
4191  */
4192
4193
4194 // nasty IE9 hack - what a pile of crap that is..
4195
4196  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4197     Range.prototype.createContextualFragment = function (html) {
4198         var doc = window.document;
4199         var container = doc.createElement("div");
4200         container.innerHTML = html;
4201         var frag = doc.createDocumentFragment(), n;
4202         while ((n = container.firstChild)) {
4203             frag.appendChild(n);
4204         }
4205         return frag;
4206     };
4207 }
4208
4209 /**
4210  * @class Roo.DomHelper
4211  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4212  * 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>.
4213  * @singleton
4214  */
4215 Roo.DomHelper = function(){
4216     var tempTableEl = null;
4217     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4218     var tableRe = /^table|tbody|tr|td$/i;
4219     var xmlns = {};
4220     // build as innerHTML where available
4221     /** @ignore */
4222     var createHtml = function(o){
4223         if(typeof o == 'string'){
4224             return o;
4225         }
4226         var b = "";
4227         if(!o.tag){
4228             o.tag = "div";
4229         }
4230         b += "<" + o.tag;
4231         for(var attr in o){
4232             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4233             if(attr == "style"){
4234                 var s = o["style"];
4235                 if(typeof s == "function"){
4236                     s = s.call();
4237                 }
4238                 if(typeof s == "string"){
4239                     b += ' style="' + s + '"';
4240                 }else if(typeof s == "object"){
4241                     b += ' style="';
4242                     for(var key in s){
4243                         if(typeof s[key] != "function"){
4244                             b += key + ":" + s[key] + ";";
4245                         }
4246                     }
4247                     b += '"';
4248                 }
4249             }else{
4250                 if(attr == "cls"){
4251                     b += ' class="' + o["cls"] + '"';
4252                 }else if(attr == "htmlFor"){
4253                     b += ' for="' + o["htmlFor"] + '"';
4254                 }else{
4255                     b += " " + attr + '="' + o[attr] + '"';
4256                 }
4257             }
4258         }
4259         if(emptyTags.test(o.tag)){
4260             b += "/>";
4261         }else{
4262             b += ">";
4263             var cn = o.children || o.cn;
4264             if(cn){
4265                 //http://bugs.kde.org/show_bug.cgi?id=71506
4266                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4267                     for(var i = 0, len = cn.length; i < len; i++) {
4268                         b += createHtml(cn[i], b);
4269                     }
4270                 }else{
4271                     b += createHtml(cn, b);
4272                 }
4273             }
4274             if(o.html){
4275                 b += o.html;
4276             }
4277             b += "</" + o.tag + ">";
4278         }
4279         return b;
4280     };
4281
4282     // build as dom
4283     /** @ignore */
4284     var createDom = function(o, parentNode){
4285          
4286         // defininition craeted..
4287         var ns = false;
4288         if (o.ns && o.ns != 'html') {
4289                
4290             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4291                 xmlns[o.ns] = o.xmlns;
4292                 ns = o.xmlns;
4293             }
4294             if (typeof(xmlns[o.ns]) == 'undefined') {
4295                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4296             }
4297             ns = xmlns[o.ns];
4298         }
4299         
4300         
4301         if (typeof(o) == 'string') {
4302             return parentNode.appendChild(document.createTextNode(o));
4303         }
4304         o.tag = o.tag || div;
4305         if (o.ns && Roo.isIE) {
4306             ns = false;
4307             o.tag = o.ns + ':' + o.tag;
4308             
4309         }
4310         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4311         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4312         for(var attr in o){
4313             
4314             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4315                     attr == "style" || typeof o[attr] == "function") { continue; }
4316                     
4317             if(attr=="cls" && Roo.isIE){
4318                 el.className = o["cls"];
4319             }else{
4320                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4321                 else { 
4322                     el[attr] = o[attr];
4323                 }
4324             }
4325         }
4326         Roo.DomHelper.applyStyles(el, o.style);
4327         var cn = o.children || o.cn;
4328         if(cn){
4329             //http://bugs.kde.org/show_bug.cgi?id=71506
4330              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4331                 for(var i = 0, len = cn.length; i < len; i++) {
4332                     createDom(cn[i], el);
4333                 }
4334             }else{
4335                 createDom(cn, el);
4336             }
4337         }
4338         if(o.html){
4339             el.innerHTML = o.html;
4340         }
4341         if(parentNode){
4342            parentNode.appendChild(el);
4343         }
4344         return el;
4345     };
4346
4347     var ieTable = function(depth, s, h, e){
4348         tempTableEl.innerHTML = [s, h, e].join('');
4349         var i = -1, el = tempTableEl;
4350         while(++i < depth){
4351             el = el.firstChild;
4352         }
4353         return el;
4354     };
4355
4356     // kill repeat to save bytes
4357     var ts = '<table>',
4358         te = '</table>',
4359         tbs = ts+'<tbody>',
4360         tbe = '</tbody>'+te,
4361         trs = tbs + '<tr>',
4362         tre = '</tr>'+tbe;
4363
4364     /**
4365      * @ignore
4366      * Nasty code for IE's broken table implementation
4367      */
4368     var insertIntoTable = function(tag, where, el, html){
4369         if(!tempTableEl){
4370             tempTableEl = document.createElement('div');
4371         }
4372         var node;
4373         var before = null;
4374         if(tag == 'td'){
4375             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4376                 return;
4377             }
4378             if(where == 'beforebegin'){
4379                 before = el;
4380                 el = el.parentNode;
4381             } else{
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384             }
4385             node = ieTable(4, trs, html, tre);
4386         }
4387         else if(tag == 'tr'){
4388             if(where == 'beforebegin'){
4389                 before = el;
4390                 el = el.parentNode;
4391                 node = ieTable(3, tbs, html, tbe);
4392             } else if(where == 'afterend'){
4393                 before = el.nextSibling;
4394                 el = el.parentNode;
4395                 node = ieTable(3, tbs, html, tbe);
4396             } else{ // INTO a TR
4397                 if(where == 'afterbegin'){
4398                     before = el.firstChild;
4399                 }
4400                 node = ieTable(4, trs, html, tre);
4401             }
4402         } else if(tag == 'tbody'){
4403             if(where == 'beforebegin'){
4404                 before = el;
4405                 el = el.parentNode;
4406                 node = ieTable(2, ts, html, te);
4407             } else if(where == 'afterend'){
4408                 before = el.nextSibling;
4409                 el = el.parentNode;
4410                 node = ieTable(2, ts, html, te);
4411             } else{
4412                 if(where == 'afterbegin'){
4413                     before = el.firstChild;
4414                 }
4415                 node = ieTable(3, tbs, html, tbe);
4416             }
4417         } else{ // TABLE
4418             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4419                 return;
4420             }
4421             if(where == 'afterbegin'){
4422                 before = el.firstChild;
4423             }
4424             node = ieTable(2, ts, html, te);
4425         }
4426         el.insertBefore(node, before);
4427         return node;
4428     };
4429
4430     return {
4431     /** True to force the use of DOM instead of html fragments @type Boolean */
4432     useDom : false,
4433
4434     /**
4435      * Returns the markup for the passed Element(s) config
4436      * @param {Object} o The Dom object spec (and children)
4437      * @return {String}
4438      */
4439     markup : function(o){
4440         return createHtml(o);
4441     },
4442
4443     /**
4444      * Applies a style specification to an element
4445      * @param {String/HTMLElement} el The element to apply styles to
4446      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4447      * a function which returns such a specification.
4448      */
4449     applyStyles : function(el, styles){
4450         if(styles){
4451            el = Roo.fly(el);
4452            if(typeof styles == "string"){
4453                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4454                var matches;
4455                while ((matches = re.exec(styles)) != null){
4456                    el.setStyle(matches[1], matches[2]);
4457                }
4458            }else if (typeof styles == "object"){
4459                for (var style in styles){
4460                   el.setStyle(style, styles[style]);
4461                }
4462            }else if (typeof styles == "function"){
4463                 Roo.DomHelper.applyStyles(el, styles.call());
4464            }
4465         }
4466     },
4467
4468     /**
4469      * Inserts an HTML fragment into the Dom
4470      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4471      * @param {HTMLElement} el The context element
4472      * @param {String} html The HTML fragmenet
4473      * @return {HTMLElement} The new node
4474      */
4475     insertHtml : function(where, el, html){
4476         where = where.toLowerCase();
4477         if(el.insertAdjacentHTML){
4478             if(tableRe.test(el.tagName)){
4479                 var rs;
4480                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4481                     return rs;
4482                 }
4483             }
4484             switch(where){
4485                 case "beforebegin":
4486                     el.insertAdjacentHTML('BeforeBegin', html);
4487                     return el.previousSibling;
4488                 case "afterbegin":
4489                     el.insertAdjacentHTML('AfterBegin', html);
4490                     return el.firstChild;
4491                 case "beforeend":
4492                     el.insertAdjacentHTML('BeforeEnd', html);
4493                     return el.lastChild;
4494                 case "afterend":
4495                     el.insertAdjacentHTML('AfterEnd', html);
4496                     return el.nextSibling;
4497             }
4498             throw 'Illegal insertion point -> "' + where + '"';
4499         }
4500         var range = el.ownerDocument.createRange();
4501         var frag;
4502         switch(where){
4503              case "beforebegin":
4504                 range.setStartBefore(el);
4505                 frag = range.createContextualFragment(html);
4506                 el.parentNode.insertBefore(frag, el);
4507                 return el.previousSibling;
4508              case "afterbegin":
4509                 if(el.firstChild){
4510                     range.setStartBefore(el.firstChild);
4511                     frag = range.createContextualFragment(html);
4512                     el.insertBefore(frag, el.firstChild);
4513                     return el.firstChild;
4514                 }else{
4515                     el.innerHTML = html;
4516                     return el.firstChild;
4517                 }
4518             case "beforeend":
4519                 if(el.lastChild){
4520                     range.setStartAfter(el.lastChild);
4521                     frag = range.createContextualFragment(html);
4522                     el.appendChild(frag);
4523                     return el.lastChild;
4524                 }else{
4525                     el.innerHTML = html;
4526                     return el.lastChild;
4527                 }
4528             case "afterend":
4529                 range.setStartAfter(el);
4530                 frag = range.createContextualFragment(html);
4531                 el.parentNode.insertBefore(frag, el.nextSibling);
4532                 return el.nextSibling;
4533             }
4534             throw 'Illegal insertion point -> "' + where + '"';
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them before el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertBefore : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "beforeBegin");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them after el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object} o The Dom object spec (and children)
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertAfter : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4557     },
4558
4559     /**
4560      * Creates new Dom element(s) and inserts them as the first child of el
4561      * @param {String/HTMLElement/Element} el The context element
4562      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4563      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4564      * @return {HTMLElement/Roo.Element} The new node
4565      */
4566     insertFirst : function(el, o, returnElement){
4567         return this.doInsert(el, o, returnElement, "afterBegin");
4568     },
4569
4570     // private
4571     doInsert : function(el, o, returnElement, pos, sibling){
4572         el = Roo.getDom(el);
4573         var newNode;
4574         if(this.useDom || o.ns){
4575             newNode = createDom(o, null);
4576             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4577         }else{
4578             var html = createHtml(o);
4579             newNode = this.insertHtml(pos, el, html);
4580         }
4581         return returnElement ? Roo.get(newNode, true) : newNode;
4582     },
4583
4584     /**
4585      * Creates new Dom element(s) and appends them to el
4586      * @param {String/HTMLElement/Element} el The context element
4587      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4588      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4589      * @return {HTMLElement/Roo.Element} The new node
4590      */
4591     append : function(el, o, returnElement){
4592         el = Roo.getDom(el);
4593         var newNode;
4594         if(this.useDom || o.ns){
4595             newNode = createDom(o, null);
4596             el.appendChild(newNode);
4597         }else{
4598             var html = createHtml(o);
4599             newNode = this.insertHtml("beforeEnd", el, html);
4600         }
4601         return returnElement ? Roo.get(newNode, true) : newNode;
4602     },
4603
4604     /**
4605      * Creates new Dom element(s) and overwrites the contents of el with them
4606      * @param {String/HTMLElement/Element} el The context element
4607      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4608      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4609      * @return {HTMLElement/Roo.Element} The new node
4610      */
4611     overwrite : function(el, o, returnElement){
4612         el = Roo.getDom(el);
4613         if (o.ns) {
4614           
4615             while (el.childNodes.length) {
4616                 el.removeChild(el.firstChild);
4617             }
4618             createDom(o, el);
4619         } else {
4620             el.innerHTML = createHtml(o);   
4621         }
4622         
4623         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4624     },
4625
4626     /**
4627      * Creates a new Roo.DomHelper.Template from the Dom object spec
4628      * @param {Object} o The Dom object spec (and children)
4629      * @return {Roo.DomHelper.Template} The new template
4630      */
4631     createTemplate : function(o){
4632         var html = createHtml(o);
4633         return new Roo.Template(html);
4634     }
4635     };
4636 }();
4637 /*
4638  * Based on:
4639  * Ext JS Library 1.1.1
4640  * Copyright(c) 2006-2007, Ext JS, LLC.
4641  *
4642  * Originally Released Under LGPL - original licence link has changed is not relivant.
4643  *
4644  * Fork - LGPL
4645  * <script type="text/javascript">
4646  */
4647  
4648 /**
4649 * @class Roo.Template
4650 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4651 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4652 * Usage:
4653 <pre><code>
4654 var t = new Roo.Template({
4655     html :  '&lt;div name="{id}"&gt;' + 
4656         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4657         '&lt;/div&gt;',
4658     myformat: function (value, allValues) {
4659         return 'XX' + value;
4660     }
4661 });
4662 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4663 </code></pre>
4664 * For more information see this blog post with examples:
4665 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4666      - Create Elements using DOM, HTML fragments and Templates</a>. 
4667 * @constructor
4668 * @param {Object} cfg - Configuration object.
4669 */
4670 Roo.Template = function(cfg){
4671     // BC!
4672     if(cfg instanceof Array){
4673         cfg = cfg.join("");
4674     }else if(arguments.length > 1){
4675         cfg = Array.prototype.join.call(arguments, "");
4676     }
4677     
4678     
4679     if (typeof(cfg) == 'object') {
4680         Roo.apply(this,cfg)
4681     } else {
4682         // bc
4683         this.html = cfg;
4684     }
4685     if (this.url) {
4686         this.load();
4687     }
4688     
4689 };
4690 Roo.Template.prototype = {
4691     
4692     /**
4693      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4694      */
4695     onLoad : false,
4696     
4697     
4698     /**
4699      * @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..
4700      *                    it should be fixed so that template is observable...
4701      */
4702     url : false,
4703     /**
4704      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4705      */
4706     html : '',
4707     
4708     
4709     compiled : false,
4710     loaded : false,
4711     /**
4712      * Returns an HTML fragment of this template with the specified values applied.
4713      * @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'})
4714      * @return {String} The HTML fragment
4715      */
4716     
4717    
4718     
4719     applyTemplate : function(values){
4720         //Roo.log(["applyTemplate", values]);
4721         try {
4722            
4723             if(this.compiled){
4724                 return this.compiled(values);
4725             }
4726             var useF = this.disableFormats !== true;
4727             var fm = Roo.util.Format, tpl = this;
4728             var fn = function(m, name, format, args){
4729                 if(format && useF){
4730                     if(format.substr(0, 5) == "this."){
4731                         return tpl.call(format.substr(5), values[name], values);
4732                     }else{
4733                         if(args){
4734                             // quoted values are required for strings in compiled templates, 
4735                             // but for non compiled we need to strip them
4736                             // quoted reversed for jsmin
4737                             var re = /^\s*['"](.*)["']\s*$/;
4738                             args = args.split(',');
4739                             for(var i = 0, len = args.length; i < len; i++){
4740                                 args[i] = args[i].replace(re, "$1");
4741                             }
4742                             args = [values[name]].concat(args);
4743                         }else{
4744                             args = [values[name]];
4745                         }
4746                         return fm[format].apply(fm, args);
4747                     }
4748                 }else{
4749                     return values[name] !== undefined ? values[name] : "";
4750                 }
4751             };
4752             return this.html.replace(this.re, fn);
4753         } catch (e) {
4754             Roo.log(e);
4755             throw e;
4756         }
4757          
4758     },
4759     
4760     loading : false,
4761       
4762     load : function ()
4763     {
4764          
4765         if (this.loading) {
4766             return;
4767         }
4768         var _t = this;
4769         
4770         this.loading = true;
4771         this.compiled = false;
4772         
4773         var cx = new Roo.data.Connection();
4774         cx.request({
4775             url : this.url,
4776             method : 'GET',
4777             success : function (response) {
4778                 _t.loading = false;
4779                 _t.url = false;
4780                 
4781                 _t.set(response.responseText,true);
4782                 _t.loaded = true;
4783                 if (_t.onLoad) {
4784                     _t.onLoad();
4785                 }
4786              },
4787             failure : function(response) {
4788                 Roo.log("Template failed to load from " + _t.url);
4789                 _t.loading = false;
4790             }
4791         });
4792     },
4793
4794     /**
4795      * Sets the HTML used as the template and optionally compiles it.
4796      * @param {String} html
4797      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4798      * @return {Roo.Template} this
4799      */
4800     set : function(html, compile){
4801         this.html = html;
4802         this.compiled = false;
4803         if(compile){
4804             this.compile();
4805         }
4806         return this;
4807     },
4808     
4809     /**
4810      * True to disable format functions (defaults to false)
4811      * @type Boolean
4812      */
4813     disableFormats : false,
4814     
4815     /**
4816     * The regular expression used to match template variables 
4817     * @type RegExp
4818     * @property 
4819     */
4820     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4821     
4822     /**
4823      * Compiles the template into an internal function, eliminating the RegEx overhead.
4824      * @return {Roo.Template} this
4825      */
4826     compile : function(){
4827         var fm = Roo.util.Format;
4828         var useF = this.disableFormats !== true;
4829         var sep = Roo.isGecko ? "+" : ",";
4830         var fn = function(m, name, format, args){
4831             if(format && useF){
4832                 args = args ? ',' + args : "";
4833                 if(format.substr(0, 5) != "this."){
4834                     format = "fm." + format + '(';
4835                 }else{
4836                     format = 'this.call("'+ format.substr(5) + '", ';
4837                     args = ", values";
4838                 }
4839             }else{
4840                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4841             }
4842             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4843         };
4844         var body;
4845         // branched to use + in gecko and [].join() in others
4846         if(Roo.isGecko){
4847             body = "this.compiled = function(values){ return '" +
4848                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4849                     "';};";
4850         }else{
4851             body = ["this.compiled = function(values){ return ['"];
4852             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4853             body.push("'].join('');};");
4854             body = body.join('');
4855         }
4856         /**
4857          * eval:var:values
4858          * eval:var:fm
4859          */
4860         eval(body);
4861         return this;
4862     },
4863     
4864     // private function used to call members
4865     call : function(fnName, value, allValues){
4866         return this[fnName](value, allValues);
4867     },
4868     
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @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'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertFirst: function(el, values, returnElement){
4877         return this.doInsert('afterBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) before el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @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'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertBefore: function(el, values, returnElement){
4888         return this.doInsert('beforeBegin', el, values, returnElement);
4889     },
4890
4891     /**
4892      * Applies the supplied values to the template and inserts the new node(s) after el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     insertAfter : function(el, values, returnElement){
4899         return this.doInsert('afterEnd', el, values, returnElement);
4900     },
4901     
4902     /**
4903      * Applies the supplied values to the template and appends the new node(s) to el.
4904      * @param {String/HTMLElement/Roo.Element} el The context element
4905      * @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'})
4906      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4907      * @return {HTMLElement/Roo.Element} The new node or Element
4908      */
4909     append : function(el, values, returnElement){
4910         return this.doInsert('beforeEnd', el, values, returnElement);
4911     },
4912
4913     doInsert : function(where, el, values, returnEl){
4914         el = Roo.getDom(el);
4915         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4916         return returnEl ? Roo.get(newNode, true) : newNode;
4917     },
4918
4919     /**
4920      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4921      * @param {String/HTMLElement/Roo.Element} el The context element
4922      * @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'})
4923      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4924      * @return {HTMLElement/Roo.Element} The new node or Element
4925      */
4926     overwrite : function(el, values, returnElement){
4927         el = Roo.getDom(el);
4928         el.innerHTML = this.applyTemplate(values);
4929         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4930     }
4931 };
4932 /**
4933  * Alias for {@link #applyTemplate}
4934  * @method
4935  */
4936 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4937
4938 // backwards compat
4939 Roo.DomHelper.Template = Roo.Template;
4940
4941 /**
4942  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4943  * @param {String/HTMLElement} el A DOM element or its id
4944  * @returns {Roo.Template} The created template
4945  * @static
4946  */
4947 Roo.Template.from = function(el){
4948     el = Roo.getDom(el);
4949     return new Roo.Template(el.value || el.innerHTML);
4950 };/*
4951  * Based on:
4952  * Ext JS Library 1.1.1
4953  * Copyright(c) 2006-2007, Ext JS, LLC.
4954  *
4955  * Originally Released Under LGPL - original licence link has changed is not relivant.
4956  *
4957  * Fork - LGPL
4958  * <script type="text/javascript">
4959  */
4960  
4961
4962 /*
4963  * This is code is also distributed under MIT license for use
4964  * with jQuery and prototype JavaScript libraries.
4965  */
4966 /**
4967  * @class Roo.DomQuery
4968 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).
4969 <p>
4970 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>
4971
4972 <p>
4973 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.
4974 </p>
4975 <h4>Element Selectors:</h4>
4976 <ul class="list">
4977     <li> <b>*</b> any element</li>
4978     <li> <b>E</b> an element with the tag E</li>
4979     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4980     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4981     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4982     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4983 </ul>
4984 <h4>Attribute Selectors:</h4>
4985 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4986 <ul class="list">
4987     <li> <b>E[foo]</b> has an attribute "foo"</li>
4988     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4989     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4990     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4991     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4992     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4993     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4994 </ul>
4995 <h4>Pseudo Classes:</h4>
4996 <ul class="list">
4997     <li> <b>E:first-child</b> E is the first child of its parent</li>
4998     <li> <b>E:last-child</b> E is the last child of its parent</li>
4999     <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>
5000     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5001     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5002     <li> <b>E:only-child</b> E is the only child of its parent</li>
5003     <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>
5004     <li> <b>E:first</b> the first E in the resultset</li>
5005     <li> <b>E:last</b> the last E in the resultset</li>
5006     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5007     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5008     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5009     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5010     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5011     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5012     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5013     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5014     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5015 </ul>
5016 <h4>CSS Value Selectors:</h4>
5017 <ul class="list">
5018     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5019     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5020     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5021     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5022     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5023     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5024 </ul>
5025  * @singleton
5026  */
5027 Roo.DomQuery = function(){
5028     var cache = {}, simpleCache = {}, valueCache = {};
5029     var nonSpace = /\S/;
5030     var trimRe = /^\s+|\s+$/g;
5031     var tplRe = /\{(\d+)\}/g;
5032     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5033     var tagTokenRe = /^(#)?([\w-\*]+)/;
5034     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5035
5036     function child(p, index){
5037         var i = 0;
5038         var n = p.firstChild;
5039         while(n){
5040             if(n.nodeType == 1){
5041                if(++i == index){
5042                    return n;
5043                }
5044             }
5045             n = n.nextSibling;
5046         }
5047         return null;
5048     };
5049
5050     function next(n){
5051         while((n = n.nextSibling) && n.nodeType != 1);
5052         return n;
5053     };
5054
5055     function prev(n){
5056         while((n = n.previousSibling) && n.nodeType != 1);
5057         return n;
5058     };
5059
5060     function children(d){
5061         var n = d.firstChild, ni = -1;
5062             while(n){
5063                 var nx = n.nextSibling;
5064                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5065                     d.removeChild(n);
5066                 }else{
5067                     n.nodeIndex = ++ni;
5068                 }
5069                 n = nx;
5070             }
5071             return this;
5072         };
5073
5074     function byClassName(c, a, v){
5075         if(!v){
5076             return c;
5077         }
5078         var r = [], ri = -1, cn;
5079         for(var i = 0, ci; ci = c[i]; i++){
5080             
5081             
5082             if((' '+
5083                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5084                  +' ').indexOf(v) != -1){
5085                 r[++ri] = ci;
5086             }
5087         }
5088         return r;
5089     };
5090
5091     function attrValue(n, attr){
5092         if(!n.tagName && typeof n.length != "undefined"){
5093             n = n[0];
5094         }
5095         if(!n){
5096             return null;
5097         }
5098         if(attr == "for"){
5099             return n.htmlFor;
5100         }
5101         if(attr == "class" || attr == "className"){
5102             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5103         }
5104         return n.getAttribute(attr) || n[attr];
5105
5106     };
5107
5108     function getNodes(ns, mode, tagName){
5109         var result = [], ri = -1, cs;
5110         if(!ns){
5111             return result;
5112         }
5113         tagName = tagName || "*";
5114         if(typeof ns.getElementsByTagName != "undefined"){
5115             ns = [ns];
5116         }
5117         if(!mode){
5118             for(var i = 0, ni; ni = ns[i]; i++){
5119                 cs = ni.getElementsByTagName(tagName);
5120                 for(var j = 0, ci; ci = cs[j]; j++){
5121                     result[++ri] = ci;
5122                 }
5123             }
5124         }else if(mode == "/" || mode == ">"){
5125             var utag = tagName.toUpperCase();
5126             for(var i = 0, ni, cn; ni = ns[i]; i++){
5127                 cn = ni.children || ni.childNodes;
5128                 for(var j = 0, cj; cj = cn[j]; j++){
5129                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5130                         result[++ri] = cj;
5131                     }
5132                 }
5133             }
5134         }else if(mode == "+"){
5135             var utag = tagName.toUpperCase();
5136             for(var i = 0, n; n = ns[i]; i++){
5137                 while((n = n.nextSibling) && n.nodeType != 1);
5138                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5139                     result[++ri] = n;
5140                 }
5141             }
5142         }else if(mode == "~"){
5143             for(var i = 0, n; n = ns[i]; i++){
5144                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5145                 if(n){
5146                     result[++ri] = n;
5147                 }
5148             }
5149         }
5150         return result;
5151     };
5152
5153     function concat(a, b){
5154         if(b.slice){
5155             return a.concat(b);
5156         }
5157         for(var i = 0, l = b.length; i < l; i++){
5158             a[a.length] = b[i];
5159         }
5160         return a;
5161     }
5162
5163     function byTag(cs, tagName){
5164         if(cs.tagName || cs == document){
5165             cs = [cs];
5166         }
5167         if(!tagName){
5168             return cs;
5169         }
5170         var r = [], ri = -1;
5171         tagName = tagName.toLowerCase();
5172         for(var i = 0, ci; ci = cs[i]; i++){
5173             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5174                 r[++ri] = ci;
5175             }
5176         }
5177         return r;
5178     };
5179
5180     function byId(cs, attr, id){
5181         if(cs.tagName || cs == document){
5182             cs = [cs];
5183         }
5184         if(!id){
5185             return cs;
5186         }
5187         var r = [], ri = -1;
5188         for(var i = 0,ci; ci = cs[i]; i++){
5189             if(ci && ci.id == id){
5190                 r[++ri] = ci;
5191                 return r;
5192             }
5193         }
5194         return r;
5195     };
5196
5197     function byAttribute(cs, attr, value, op, custom){
5198         var r = [], ri = -1, st = custom=="{";
5199         var f = Roo.DomQuery.operators[op];
5200         for(var i = 0, ci; ci = cs[i]; i++){
5201             var a;
5202             if(st){
5203                 a = Roo.DomQuery.getStyle(ci, attr);
5204             }
5205             else if(attr == "class" || attr == "className"){
5206                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5207             }else if(attr == "for"){
5208                 a = ci.htmlFor;
5209             }else if(attr == "href"){
5210                 a = ci.getAttribute("href", 2);
5211             }else{
5212                 a = ci.getAttribute(attr);
5213             }
5214             if((f && f(a, value)) || (!f && a)){
5215                 r[++ri] = ci;
5216             }
5217         }
5218         return r;
5219     };
5220
5221     function byPseudo(cs, name, value){
5222         return Roo.DomQuery.pseudos[name](cs, value);
5223     };
5224
5225     // This is for IE MSXML which does not support expandos.
5226     // IE runs the same speed using setAttribute, however FF slows way down
5227     // and Safari completely fails so they need to continue to use expandos.
5228     var isIE = window.ActiveXObject ? true : false;
5229
5230     // this eval is stop the compressor from
5231     // renaming the variable to something shorter
5232     
5233     /** eval:var:batch */
5234     var batch = 30803; 
5235
5236     var key = 30803;
5237
5238     function nodupIEXml(cs){
5239         var d = ++key;
5240         cs[0].setAttribute("_nodup", d);
5241         var r = [cs[0]];
5242         for(var i = 1, len = cs.length; i < len; i++){
5243             var c = cs[i];
5244             if(!c.getAttribute("_nodup") != d){
5245                 c.setAttribute("_nodup", d);
5246                 r[r.length] = c;
5247             }
5248         }
5249         for(var i = 0, len = cs.length; i < len; i++){
5250             cs[i].removeAttribute("_nodup");
5251         }
5252         return r;
5253     }
5254
5255     function nodup(cs){
5256         if(!cs){
5257             return [];
5258         }
5259         var len = cs.length, c, i, r = cs, cj, ri = -1;
5260         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5261             return cs;
5262         }
5263         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5264             return nodupIEXml(cs);
5265         }
5266         var d = ++key;
5267         cs[0]._nodup = d;
5268         for(i = 1; c = cs[i]; i++){
5269             if(c._nodup != d){
5270                 c._nodup = d;
5271             }else{
5272                 r = [];
5273                 for(var j = 0; j < i; j++){
5274                     r[++ri] = cs[j];
5275                 }
5276                 for(j = i+1; cj = cs[j]; j++){
5277                     if(cj._nodup != d){
5278                         cj._nodup = d;
5279                         r[++ri] = cj;
5280                     }
5281                 }
5282                 return r;
5283             }
5284         }
5285         return r;
5286     }
5287
5288     function quickDiffIEXml(c1, c2){
5289         var d = ++key;
5290         for(var i = 0, len = c1.length; i < len; i++){
5291             c1[i].setAttribute("_qdiff", d);
5292         }
5293         var r = [];
5294         for(var i = 0, len = c2.length; i < len; i++){
5295             if(c2[i].getAttribute("_qdiff") != d){
5296                 r[r.length] = c2[i];
5297             }
5298         }
5299         for(var i = 0, len = c1.length; i < len; i++){
5300            c1[i].removeAttribute("_qdiff");
5301         }
5302         return r;
5303     }
5304
5305     function quickDiff(c1, c2){
5306         var len1 = c1.length;
5307         if(!len1){
5308             return c2;
5309         }
5310         if(isIE && c1[0].selectSingleNode){
5311             return quickDiffIEXml(c1, c2);
5312         }
5313         var d = ++key;
5314         for(var i = 0; i < len1; i++){
5315             c1[i]._qdiff = d;
5316         }
5317         var r = [];
5318         for(var i = 0, len = c2.length; i < len; i++){
5319             if(c2[i]._qdiff != d){
5320                 r[r.length] = c2[i];
5321             }
5322         }
5323         return r;
5324     }
5325
5326     function quickId(ns, mode, root, id){
5327         if(ns == root){
5328            var d = root.ownerDocument || root;
5329            return d.getElementById(id);
5330         }
5331         ns = getNodes(ns, mode, "*");
5332         return byId(ns, null, id);
5333     }
5334
5335     return {
5336         getStyle : function(el, name){
5337             return Roo.fly(el).getStyle(name);
5338         },
5339         /**
5340          * Compiles a selector/xpath query into a reusable function. The returned function
5341          * takes one parameter "root" (optional), which is the context node from where the query should start.
5342          * @param {String} selector The selector/xpath query
5343          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5344          * @return {Function}
5345          */
5346         compile : function(path, type){
5347             type = type || "select";
5348             
5349             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5350             var q = path, mode, lq;
5351             var tk = Roo.DomQuery.matchers;
5352             var tklen = tk.length;
5353             var mm;
5354
5355             // accept leading mode switch
5356             var lmode = q.match(modeRe);
5357             if(lmode && lmode[1]){
5358                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5359                 q = q.replace(lmode[1], "");
5360             }
5361             // strip leading slashes
5362             while(path.substr(0, 1)=="/"){
5363                 path = path.substr(1);
5364             }
5365
5366             while(q && lq != q){
5367                 lq = q;
5368                 var tm = q.match(tagTokenRe);
5369                 if(type == "select"){
5370                     if(tm){
5371                         if(tm[1] == "#"){
5372                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5373                         }else{
5374                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5375                         }
5376                         q = q.replace(tm[0], "");
5377                     }else if(q.substr(0, 1) != '@'){
5378                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5379                     }
5380                 }else{
5381                     if(tm){
5382                         if(tm[1] == "#"){
5383                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5384                         }else{
5385                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5386                         }
5387                         q = q.replace(tm[0], "");
5388                     }
5389                 }
5390                 while(!(mm = q.match(modeRe))){
5391                     var matched = false;
5392                     for(var j = 0; j < tklen; j++){
5393                         var t = tk[j];
5394                         var m = q.match(t.re);
5395                         if(m){
5396                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5397                                                     return m[i];
5398                                                 });
5399                             q = q.replace(m[0], "");
5400                             matched = true;
5401                             break;
5402                         }
5403                     }
5404                     // prevent infinite loop on bad selector
5405                     if(!matched){
5406                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5407                     }
5408                 }
5409                 if(mm[1]){
5410                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5411                     q = q.replace(mm[1], "");
5412                 }
5413             }
5414             fn[fn.length] = "return nodup(n);\n}";
5415             
5416              /** 
5417               * list of variables that need from compression as they are used by eval.
5418              *  eval:var:batch 
5419              *  eval:var:nodup
5420              *  eval:var:byTag
5421              *  eval:var:ById
5422              *  eval:var:getNodes
5423              *  eval:var:quickId
5424              *  eval:var:mode
5425              *  eval:var:root
5426              *  eval:var:n
5427              *  eval:var:byClassName
5428              *  eval:var:byPseudo
5429              *  eval:var:byAttribute
5430              *  eval:var:attrValue
5431              * 
5432              **/ 
5433             eval(fn.join(""));
5434             return f;
5435         },
5436
5437         /**
5438          * Selects a group of elements.
5439          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5440          * @param {Node} root (optional) The start of the query (defaults to document).
5441          * @return {Array}
5442          */
5443         select : function(path, root, type){
5444             if(!root || root == document){
5445                 root = document;
5446             }
5447             if(typeof root == "string"){
5448                 root = document.getElementById(root);
5449             }
5450             var paths = path.split(",");
5451             var results = [];
5452             for(var i = 0, len = paths.length; i < len; i++){
5453                 var p = paths[i].replace(trimRe, "");
5454                 if(!cache[p]){
5455                     cache[p] = Roo.DomQuery.compile(p);
5456                     if(!cache[p]){
5457                         throw p + " is not a valid selector";
5458                     }
5459                 }
5460                 var result = cache[p](root);
5461                 if(result && result != document){
5462                     results = results.concat(result);
5463                 }
5464             }
5465             if(paths.length > 1){
5466                 return nodup(results);
5467             }
5468             return results;
5469         },
5470
5471         /**
5472          * Selects a single element.
5473          * @param {String} selector The selector/xpath query
5474          * @param {Node} root (optional) The start of the query (defaults to document).
5475          * @return {Element}
5476          */
5477         selectNode : function(path, root){
5478             return Roo.DomQuery.select(path, root)[0];
5479         },
5480
5481         /**
5482          * Selects the value of a node, optionally replacing null with the defaultValue.
5483          * @param {String} selector The selector/xpath query
5484          * @param {Node} root (optional) The start of the query (defaults to document).
5485          * @param {String} defaultValue
5486          */
5487         selectValue : function(path, root, defaultValue){
5488             path = path.replace(trimRe, "");
5489             if(!valueCache[path]){
5490                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5491             }
5492             var n = valueCache[path](root);
5493             n = n[0] ? n[0] : n;
5494             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5495             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5496         },
5497
5498         /**
5499          * Selects the value of a node, parsing integers and floats.
5500          * @param {String} selector The selector/xpath query
5501          * @param {Node} root (optional) The start of the query (defaults to document).
5502          * @param {Number} defaultValue
5503          * @return {Number}
5504          */
5505         selectNumber : function(path, root, defaultValue){
5506             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5507             return parseFloat(v);
5508         },
5509
5510         /**
5511          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5512          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5513          * @param {String} selector The simple selector to test
5514          * @return {Boolean}
5515          */
5516         is : function(el, ss){
5517             if(typeof el == "string"){
5518                 el = document.getElementById(el);
5519             }
5520             var isArray = (el instanceof Array);
5521             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5522             return isArray ? (result.length == el.length) : (result.length > 0);
5523         },
5524
5525         /**
5526          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5527          * @param {Array} el An array of elements to filter
5528          * @param {String} selector The simple selector to test
5529          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5530          * the selector instead of the ones that match
5531          * @return {Array}
5532          */
5533         filter : function(els, ss, nonMatches){
5534             ss = ss.replace(trimRe, "");
5535             if(!simpleCache[ss]){
5536                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5537             }
5538             var result = simpleCache[ss](els);
5539             return nonMatches ? quickDiff(result, els) : result;
5540         },
5541
5542         /**
5543          * Collection of matching regular expressions and code snippets.
5544          */
5545         matchers : [{
5546                 re: /^\.([\w-]+)/,
5547                 select: 'n = byClassName(n, null, " {1} ");'
5548             }, {
5549                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5550                 select: 'n = byPseudo(n, "{1}", "{2}");'
5551             },{
5552                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5553                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5554             }, {
5555                 re: /^#([\w-]+)/,
5556                 select: 'n = byId(n, null, "{1}");'
5557             },{
5558                 re: /^@([\w-]+)/,
5559                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5560             }
5561         ],
5562
5563         /**
5564          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5565          * 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;.
5566          */
5567         operators : {
5568             "=" : function(a, v){
5569                 return a == v;
5570             },
5571             "!=" : function(a, v){
5572                 return a != v;
5573             },
5574             "^=" : function(a, v){
5575                 return a && a.substr(0, v.length) == v;
5576             },
5577             "$=" : function(a, v){
5578                 return a && a.substr(a.length-v.length) == v;
5579             },
5580             "*=" : function(a, v){
5581                 return a && a.indexOf(v) !== -1;
5582             },
5583             "%=" : function(a, v){
5584                 return (a % v) == 0;
5585             },
5586             "|=" : function(a, v){
5587                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5588             },
5589             "~=" : function(a, v){
5590                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5591             }
5592         },
5593
5594         /**
5595          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5596          * and the argument (if any) supplied in the selector.
5597          */
5598         pseudos : {
5599             "first-child" : function(c){
5600                 var r = [], ri = -1, n;
5601                 for(var i = 0, ci; ci = n = c[i]; i++){
5602                     while((n = n.previousSibling) && n.nodeType != 1);
5603                     if(!n){
5604                         r[++ri] = ci;
5605                     }
5606                 }
5607                 return r;
5608             },
5609
5610             "last-child" : function(c){
5611                 var r = [], ri = -1, n;
5612                 for(var i = 0, ci; ci = n = c[i]; i++){
5613                     while((n = n.nextSibling) && n.nodeType != 1);
5614                     if(!n){
5615                         r[++ri] = ci;
5616                     }
5617                 }
5618                 return r;
5619             },
5620
5621             "nth-child" : function(c, a) {
5622                 var r = [], ri = -1;
5623                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5624                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5625                 for(var i = 0, n; n = c[i]; i++){
5626                     var pn = n.parentNode;
5627                     if (batch != pn._batch) {
5628                         var j = 0;
5629                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5630                             if(cn.nodeType == 1){
5631                                cn.nodeIndex = ++j;
5632                             }
5633                         }
5634                         pn._batch = batch;
5635                     }
5636                     if (f == 1) {
5637                         if (l == 0 || n.nodeIndex == l){
5638                             r[++ri] = n;
5639                         }
5640                     } else if ((n.nodeIndex + l) % f == 0){
5641                         r[++ri] = n;
5642                     }
5643                 }
5644
5645                 return r;
5646             },
5647
5648             "only-child" : function(c){
5649                 var r = [], ri = -1;;
5650                 for(var i = 0, ci; ci = c[i]; i++){
5651                     if(!prev(ci) && !next(ci)){
5652                         r[++ri] = ci;
5653                     }
5654                 }
5655                 return r;
5656             },
5657
5658             "empty" : function(c){
5659                 var r = [], ri = -1;
5660                 for(var i = 0, ci; ci = c[i]; i++){
5661                     var cns = ci.childNodes, j = 0, cn, empty = true;
5662                     while(cn = cns[j]){
5663                         ++j;
5664                         if(cn.nodeType == 1 || cn.nodeType == 3){
5665                             empty = false;
5666                             break;
5667                         }
5668                     }
5669                     if(empty){
5670                         r[++ri] = ci;
5671                     }
5672                 }
5673                 return r;
5674             },
5675
5676             "contains" : function(c, v){
5677                 var r = [], ri = -1;
5678                 for(var i = 0, ci; ci = c[i]; i++){
5679                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5680                         r[++ri] = ci;
5681                     }
5682                 }
5683                 return r;
5684             },
5685
5686             "nodeValue" : function(c, v){
5687                 var r = [], ri = -1;
5688                 for(var i = 0, ci; ci = c[i]; i++){
5689                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5690                         r[++ri] = ci;
5691                     }
5692                 }
5693                 return r;
5694             },
5695
5696             "checked" : function(c){
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     if(ci.checked == true){
5700                         r[++ri] = ci;
5701                     }
5702                 }
5703                 return r;
5704             },
5705
5706             "not" : function(c, ss){
5707                 return Roo.DomQuery.filter(c, ss, true);
5708             },
5709
5710             "odd" : function(c){
5711                 return this["nth-child"](c, "odd");
5712             },
5713
5714             "even" : function(c){
5715                 return this["nth-child"](c, "even");
5716             },
5717
5718             "nth" : function(c, a){
5719                 return c[a-1] || [];
5720             },
5721
5722             "first" : function(c){
5723                 return c[0] || [];
5724             },
5725
5726             "last" : function(c){
5727                 return c[c.length-1] || [];
5728             },
5729
5730             "has" : function(c, ss){
5731                 var s = Roo.DomQuery.select;
5732                 var r = [], ri = -1;
5733                 for(var i = 0, ci; ci = c[i]; i++){
5734                     if(s(ss, ci).length > 0){
5735                         r[++ri] = ci;
5736                     }
5737                 }
5738                 return r;
5739             },
5740
5741             "next" : function(c, ss){
5742                 var is = Roo.DomQuery.is;
5743                 var r = [], ri = -1;
5744                 for(var i = 0, ci; ci = c[i]; i++){
5745                     var n = next(ci);
5746                     if(n && is(n, ss)){
5747                         r[++ri] = ci;
5748                     }
5749                 }
5750                 return r;
5751             },
5752
5753             "prev" : function(c, ss){
5754                 var is = Roo.DomQuery.is;
5755                 var r = [], ri = -1;
5756                 for(var i = 0, ci; ci = c[i]; i++){
5757                     var n = prev(ci);
5758                     if(n && is(n, ss)){
5759                         r[++ri] = ci;
5760                     }
5761                 }
5762                 return r;
5763             }
5764         }
5765     };
5766 }();
5767
5768 /**
5769  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5770  * @param {String} path The selector/xpath query
5771  * @param {Node} root (optional) The start of the query (defaults to document).
5772  * @return {Array}
5773  * @member Roo
5774  * @method query
5775  */
5776 Roo.query = Roo.DomQuery.select;
5777 /*
5778  * Based on:
5779  * Ext JS Library 1.1.1
5780  * Copyright(c) 2006-2007, Ext JS, LLC.
5781  *
5782  * Originally Released Under LGPL - original licence link has changed is not relivant.
5783  *
5784  * Fork - LGPL
5785  * <script type="text/javascript">
5786  */
5787
5788 /**
5789  * @class Roo.util.Observable
5790  * Base class that provides a common interface for publishing events. Subclasses are expected to
5791  * to have a property "events" with all the events defined.<br>
5792  * For example:
5793  * <pre><code>
5794  Employee = function(name){
5795     this.name = name;
5796     this.addEvents({
5797         "fired" : true,
5798         "quit" : true
5799     });
5800  }
5801  Roo.extend(Employee, Roo.util.Observable);
5802 </code></pre>
5803  * @param {Object} config properties to use (incuding events / listeners)
5804  */
5805
5806 Roo.util.Observable = function(cfg){
5807     
5808     cfg = cfg|| {};
5809     this.addEvents(cfg.events || {});
5810     if (cfg.events) {
5811         delete cfg.events; // make sure
5812     }
5813      
5814     Roo.apply(this, cfg);
5815     
5816     if(this.listeners){
5817         this.on(this.listeners);
5818         delete this.listeners;
5819     }
5820 };
5821 Roo.util.Observable.prototype = {
5822     /** 
5823  * @cfg {Object} listeners  list of events and functions to call for this object, 
5824  * For example :
5825  * <pre><code>
5826     listeners :  { 
5827        'click' : function(e) {
5828            ..... 
5829         } ,
5830         .... 
5831     } 
5832   </code></pre>
5833  */
5834     
5835     
5836     /**
5837      * Fires the specified event with the passed parameters (minus the event name).
5838      * @param {String} eventName
5839      * @param {Object...} args Variable number of parameters are passed to handlers
5840      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5841      */
5842     fireEvent : function(){
5843         var ce = this.events[arguments[0].toLowerCase()];
5844         if(typeof ce == "object"){
5845             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5846         }else{
5847             return true;
5848         }
5849     },
5850
5851     // private
5852     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5853
5854     /**
5855      * Appends an event handler to this component
5856      * @param {String}   eventName The type of event to listen for
5857      * @param {Function} handler The method the event invokes
5858      * @param {Object}   scope (optional) The scope in which to execute the handler
5859      * function. The handler function's "this" context.
5860      * @param {Object}   options (optional) An object containing handler configuration
5861      * properties. This may contain any of the following properties:<ul>
5862      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5863      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5864      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5865      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5866      * by the specified number of milliseconds. If the event fires again within that time, the original
5867      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5868      * </ul><br>
5869      * <p>
5870      * <b>Combining Options</b><br>
5871      * Using the options argument, it is possible to combine different types of listeners:<br>
5872      * <br>
5873      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5874                 <pre><code>
5875                 el.on('click', this.onClick, this, {
5876                         single: true,
5877                 delay: 100,
5878                 forumId: 4
5879                 });
5880                 </code></pre>
5881      * <p>
5882      * <b>Attaching multiple handlers in 1 call</b><br>
5883      * The method also allows for a single argument to be passed which is a config object containing properties
5884      * which specify multiple handlers.
5885      * <pre><code>
5886                 el.on({
5887                         'click': {
5888                         fn: this.onClick,
5889                         scope: this,
5890                         delay: 100
5891                 }, 
5892                 'mouseover': {
5893                         fn: this.onMouseOver,
5894                         scope: this
5895                 },
5896                 'mouseout': {
5897                         fn: this.onMouseOut,
5898                         scope: this
5899                 }
5900                 });
5901                 </code></pre>
5902      * <p>
5903      * Or a shorthand syntax which passes the same scope object to all handlers:
5904         <pre><code>
5905                 el.on({
5906                         'click': this.onClick,
5907                 'mouseover': this.onMouseOver,
5908                 'mouseout': this.onMouseOut,
5909                 scope: this
5910                 });
5911                 </code></pre>
5912      */
5913     addListener : function(eventName, fn, scope, o){
5914         if(typeof eventName == "object"){
5915             o = eventName;
5916             for(var e in o){
5917                 if(this.filterOptRe.test(e)){
5918                     continue;
5919                 }
5920                 if(typeof o[e] == "function"){
5921                     // shared options
5922                     this.addListener(e, o[e], o.scope,  o);
5923                 }else{
5924                     // individual options
5925                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5926                 }
5927             }
5928             return;
5929         }
5930         o = (!o || typeof o == "boolean") ? {} : o;
5931         eventName = eventName.toLowerCase();
5932         var ce = this.events[eventName] || true;
5933         if(typeof ce == "boolean"){
5934             ce = new Roo.util.Event(this, eventName);
5935             this.events[eventName] = ce;
5936         }
5937         ce.addListener(fn, scope, o);
5938     },
5939
5940     /**
5941      * Removes a listener
5942      * @param {String}   eventName     The type of event to listen for
5943      * @param {Function} handler        The handler to remove
5944      * @param {Object}   scope  (optional) The scope (this object) for the handler
5945      */
5946     removeListener : function(eventName, fn, scope){
5947         var ce = this.events[eventName.toLowerCase()];
5948         if(typeof ce == "object"){
5949             ce.removeListener(fn, scope);
5950         }
5951     },
5952
5953     /**
5954      * Removes all listeners for this object
5955      */
5956     purgeListeners : function(){
5957         for(var evt in this.events){
5958             if(typeof this.events[evt] == "object"){
5959                  this.events[evt].clearListeners();
5960             }
5961         }
5962     },
5963
5964     relayEvents : function(o, events){
5965         var createHandler = function(ename){
5966             return function(){
5967                  
5968                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5969             };
5970         };
5971         for(var i = 0, len = events.length; i < len; i++){
5972             var ename = events[i];
5973             if(!this.events[ename]){
5974                 this.events[ename] = true;
5975             };
5976             o.on(ename, createHandler(ename), this);
5977         }
5978     },
5979
5980     /**
5981      * Used to define events on this Observable
5982      * @param {Object} object The object with the events defined
5983      */
5984     addEvents : function(o){
5985         if(!this.events){
5986             this.events = {};
5987         }
5988         Roo.applyIf(this.events, o);
5989     },
5990
5991     /**
5992      * Checks to see if this object has any listeners for a specified event
5993      * @param {String} eventName The name of the event to check for
5994      * @return {Boolean} True if the event is being listened for, else false
5995      */
5996     hasListener : function(eventName){
5997         var e = this.events[eventName];
5998         return typeof e == "object" && e.listeners.length > 0;
5999     }
6000 };
6001 /**
6002  * Appends an event handler to this element (shorthand for addListener)
6003  * @param {String}   eventName     The type of event to listen for
6004  * @param {Function} handler        The method the event invokes
6005  * @param {Object}   scope (optional) The scope in which to execute the handler
6006  * function. The handler function's "this" context.
6007  * @param {Object}   options  (optional)
6008  * @method
6009  */
6010 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6011 /**
6012  * Removes a listener (shorthand for removeListener)
6013  * @param {String}   eventName     The type of event to listen for
6014  * @param {Function} handler        The handler to remove
6015  * @param {Object}   scope  (optional) The scope (this object) for the handler
6016  * @method
6017  */
6018 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6019
6020 /**
6021  * Starts capture on the specified Observable. All events will be passed
6022  * to the supplied function with the event name + standard signature of the event
6023  * <b>before</b> the event is fired. If the supplied function returns false,
6024  * the event will not fire.
6025  * @param {Observable} o The Observable to capture
6026  * @param {Function} fn The function to call
6027  * @param {Object} scope (optional) The scope (this object) for the fn
6028  * @static
6029  */
6030 Roo.util.Observable.capture = function(o, fn, scope){
6031     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6032 };
6033
6034 /**
6035  * Removes <b>all</b> added captures from the Observable.
6036  * @param {Observable} o The Observable to release
6037  * @static
6038  */
6039 Roo.util.Observable.releaseCapture = function(o){
6040     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6041 };
6042
6043 (function(){
6044
6045     var createBuffered = function(h, o, scope){
6046         var task = new Roo.util.DelayedTask();
6047         return function(){
6048             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6049         };
6050     };
6051
6052     var createSingle = function(h, e, fn, scope){
6053         return function(){
6054             e.removeListener(fn, scope);
6055             return h.apply(scope, arguments);
6056         };
6057     };
6058
6059     var createDelayed = function(h, o, scope){
6060         return function(){
6061             var args = Array.prototype.slice.call(arguments, 0);
6062             setTimeout(function(){
6063                 h.apply(scope, args);
6064             }, o.delay || 10);
6065         };
6066     };
6067
6068     Roo.util.Event = function(obj, name){
6069         this.name = name;
6070         this.obj = obj;
6071         this.listeners = [];
6072     };
6073
6074     Roo.util.Event.prototype = {
6075         addListener : function(fn, scope, options){
6076             var o = options || {};
6077             scope = scope || this.obj;
6078             if(!this.isListening(fn, scope)){
6079                 var l = {fn: fn, scope: scope, options: o};
6080                 var h = fn;
6081                 if(o.delay){
6082                     h = createDelayed(h, o, scope);
6083                 }
6084                 if(o.single){
6085                     h = createSingle(h, this, fn, scope);
6086                 }
6087                 if(o.buffer){
6088                     h = createBuffered(h, o, scope);
6089                 }
6090                 l.fireFn = h;
6091                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6092                     this.listeners.push(l);
6093                 }else{
6094                     this.listeners = this.listeners.slice(0);
6095                     this.listeners.push(l);
6096                 }
6097             }
6098         },
6099
6100         findListener : function(fn, scope){
6101             scope = scope || this.obj;
6102             var ls = this.listeners;
6103             for(var i = 0, len = ls.length; i < len; i++){
6104                 var l = ls[i];
6105                 if(l.fn == fn && l.scope == scope){
6106                     return i;
6107                 }
6108             }
6109             return -1;
6110         },
6111
6112         isListening : function(fn, scope){
6113             return this.findListener(fn, scope) != -1;
6114         },
6115
6116         removeListener : function(fn, scope){
6117             var index;
6118             if((index = this.findListener(fn, scope)) != -1){
6119                 if(!this.firing){
6120                     this.listeners.splice(index, 1);
6121                 }else{
6122                     this.listeners = this.listeners.slice(0);
6123                     this.listeners.splice(index, 1);
6124                 }
6125                 return true;
6126             }
6127             return false;
6128         },
6129
6130         clearListeners : function(){
6131             this.listeners = [];
6132         },
6133
6134         fire : function(){
6135             var ls = this.listeners, scope, len = ls.length;
6136             if(len > 0){
6137                 this.firing = true;
6138                 var args = Array.prototype.slice.call(arguments, 0);                
6139                 for(var i = 0; i < len; i++){
6140                     var l = ls[i];
6141                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6142                         this.firing = false;
6143                         return false;
6144                     }
6145                 }
6146                 this.firing = false;
6147             }
6148             return true;
6149         }
6150     };
6151 })();/*
6152  * RooJS Library 
6153  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6154  *
6155  * Licence LGPL 
6156  *
6157  */
6158  
6159 /**
6160  * @class Roo.Document
6161  * @extends Roo.util.Observable
6162  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6163  * 
6164  * @param {Object} config the methods and properties of the 'base' class for the application.
6165  * 
6166  *  Generic Page handler - implement this to start your app..
6167  * 
6168  * eg.
6169  *  MyProject = new Roo.Document({
6170         events : {
6171             'load' : true // your events..
6172         },
6173         listeners : {
6174             'ready' : function() {
6175                 // fired on Roo.onReady()
6176             }
6177         }
6178  * 
6179  */
6180 Roo.Document = function(cfg) {
6181      
6182     this.addEvents({ 
6183         'ready' : true
6184     });
6185     Roo.util.Observable.call(this,cfg);
6186     
6187     var _this = this;
6188     
6189     Roo.onReady(function() {
6190         _this.fireEvent('ready');
6191     },null,false);
6192     
6193     
6194 }
6195
6196 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6197  * Based on:
6198  * Ext JS Library 1.1.1
6199  * Copyright(c) 2006-2007, Ext JS, LLC.
6200  *
6201  * Originally Released Under LGPL - original licence link has changed is not relivant.
6202  *
6203  * Fork - LGPL
6204  * <script type="text/javascript">
6205  */
6206
6207 /**
6208  * @class Roo.EventManager
6209  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6210  * several useful events directly.
6211  * See {@link Roo.EventObject} for more details on normalized event objects.
6212  * @singleton
6213  */
6214 Roo.EventManager = function(){
6215     var docReadyEvent, docReadyProcId, docReadyState = false;
6216     var resizeEvent, resizeTask, textEvent, textSize;
6217     var E = Roo.lib.Event;
6218     var D = Roo.lib.Dom;
6219
6220     
6221     
6222
6223     var fireDocReady = function(){
6224         if(!docReadyState){
6225             docReadyState = true;
6226             Roo.isReady = true;
6227             if(docReadyProcId){
6228                 clearInterval(docReadyProcId);
6229             }
6230             if(Roo.isGecko || Roo.isOpera) {
6231                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6232             }
6233             if(Roo.isIE){
6234                 var defer = document.getElementById("ie-deferred-loader");
6235                 if(defer){
6236                     defer.onreadystatechange = null;
6237                     defer.parentNode.removeChild(defer);
6238                 }
6239             }
6240             if(docReadyEvent){
6241                 docReadyEvent.fire();
6242                 docReadyEvent.clearListeners();
6243             }
6244         }
6245     };
6246     
6247     var initDocReady = function(){
6248         docReadyEvent = new Roo.util.Event();
6249         if(Roo.isGecko || Roo.isOpera) {
6250             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6251         }else if(Roo.isIE){
6252             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6253             var defer = document.getElementById("ie-deferred-loader");
6254             defer.onreadystatechange = function(){
6255                 if(this.readyState == "complete"){
6256                     fireDocReady();
6257                 }
6258             };
6259         }else if(Roo.isSafari){ 
6260             docReadyProcId = setInterval(function(){
6261                 var rs = document.readyState;
6262                 if(rs == "complete") {
6263                     fireDocReady();     
6264                  }
6265             }, 10);
6266         }
6267         // no matter what, make sure it fires on load
6268         E.on(window, "load", fireDocReady);
6269     };
6270
6271     var createBuffered = function(h, o){
6272         var task = new Roo.util.DelayedTask(h);
6273         return function(e){
6274             // create new event object impl so new events don't wipe out properties
6275             e = new Roo.EventObjectImpl(e);
6276             task.delay(o.buffer, h, null, [e]);
6277         };
6278     };
6279
6280     var createSingle = function(h, el, ename, fn){
6281         return function(e){
6282             Roo.EventManager.removeListener(el, ename, fn);
6283             h(e);
6284         };
6285     };
6286
6287     var createDelayed = function(h, o){
6288         return function(e){
6289             // create new event object impl so new events don't wipe out properties
6290             e = new Roo.EventObjectImpl(e);
6291             setTimeout(function(){
6292                 h(e);
6293             }, o.delay || 10);
6294         };
6295     };
6296     var transitionEndVal = false;
6297     
6298     var transitionEnd = function()
6299     {
6300         if (transitionEndVal) {
6301             return transitionEndVal;
6302         }
6303         var el = document.createElement('div');
6304
6305         var transEndEventNames = {
6306             WebkitTransition : 'webkitTransitionEnd',
6307             MozTransition    : 'transitionend',
6308             OTransition      : 'oTransitionEnd otransitionend',
6309             transition       : 'transitionend'
6310         };
6311     
6312         for (var name in transEndEventNames) {
6313             if (el.style[name] !== undefined) {
6314                 transitionEndVal = transEndEventNames[name];
6315                 return  transitionEndVal ;
6316             }
6317         }
6318     }
6319     
6320   
6321
6322     var listen = function(element, ename, opt, fn, scope)
6323     {
6324         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6325         fn = fn || o.fn; scope = scope || o.scope;
6326         var el = Roo.getDom(element);
6327         
6328         
6329         if(!el){
6330             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6331         }
6332         
6333         if (ename == 'transitionend') {
6334             ename = transitionEnd();
6335         }
6336         var h = function(e){
6337             e = Roo.EventObject.setEvent(e);
6338             var t;
6339             if(o.delegate){
6340                 t = e.getTarget(o.delegate, el);
6341                 if(!t){
6342                     return;
6343                 }
6344             }else{
6345                 t = e.target;
6346             }
6347             if(o.stopEvent === true){
6348                 e.stopEvent();
6349             }
6350             if(o.preventDefault === true){
6351                e.preventDefault();
6352             }
6353             if(o.stopPropagation === true){
6354                 e.stopPropagation();
6355             }
6356
6357             if(o.normalized === false){
6358                 e = e.browserEvent;
6359             }
6360
6361             fn.call(scope || el, e, t, o);
6362         };
6363         if(o.delay){
6364             h = createDelayed(h, o);
6365         }
6366         if(o.single){
6367             h = createSingle(h, el, ename, fn);
6368         }
6369         if(o.buffer){
6370             h = createBuffered(h, o);
6371         }
6372         
6373         fn._handlers = fn._handlers || [];
6374         
6375         
6376         fn._handlers.push([Roo.id(el), ename, h]);
6377         
6378         
6379          
6380         E.on(el, ename, h); // this adds the actuall listener to the object..
6381         
6382         
6383         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6384             el.addEventListener("DOMMouseScroll", h, false);
6385             E.on(window, 'unload', function(){
6386                 el.removeEventListener("DOMMouseScroll", h, false);
6387             });
6388         }
6389         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6390             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6391         }
6392         return h;
6393     };
6394
6395     var stopListening = function(el, ename, fn){
6396         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6397         if(hds){
6398             for(var i = 0, len = hds.length; i < len; i++){
6399                 var h = hds[i];
6400                 if(h[0] == id && h[1] == ename){
6401                     hd = h[2];
6402                     hds.splice(i, 1);
6403                     break;
6404                 }
6405             }
6406         }
6407         E.un(el, ename, hd);
6408         el = Roo.getDom(el);
6409         if(ename == "mousewheel" && el.addEventListener){
6410             el.removeEventListener("DOMMouseScroll", hd, false);
6411         }
6412         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6413             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6414         }
6415     };
6416
6417     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6418     
6419     var pub = {
6420         
6421         
6422         /** 
6423          * Fix for doc tools
6424          * @scope Roo.EventManager
6425          */
6426         
6427         
6428         /** 
6429          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6430          * object with a Roo.EventObject
6431          * @param {Function} fn        The method the event invokes
6432          * @param {Object}   scope    An object that becomes the scope of the handler
6433          * @param {boolean}  override If true, the obj passed in becomes
6434          *                             the execution scope of the listener
6435          * @return {Function} The wrapped function
6436          * @deprecated
6437          */
6438         wrap : function(fn, scope, override){
6439             return function(e){
6440                 Roo.EventObject.setEvent(e);
6441                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6442             };
6443         },
6444         
6445         /**
6446      * Appends an event handler to an element (shorthand for addListener)
6447      * @param {String/HTMLElement}   element        The html element or id to assign the
6448      * @param {String}   eventName The type of event to listen for
6449      * @param {Function} handler The method the event invokes
6450      * @param {Object}   scope (optional) The scope in which to execute the handler
6451      * function. The handler function's "this" context.
6452      * @param {Object}   options (optional) An object containing handler configuration
6453      * properties. This may contain any of the following properties:<ul>
6454      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6455      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6456      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6457      * <li>preventDefault {Boolean} True to prevent the default action</li>
6458      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6459      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6460      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6461      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6462      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6463      * by the specified number of milliseconds. If the event fires again within that time, the original
6464      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6465      * </ul><br>
6466      * <p>
6467      * <b>Combining Options</b><br>
6468      * Using the options argument, it is possible to combine different types of listeners:<br>
6469      * <br>
6470      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6471      * Code:<pre><code>
6472 el.on('click', this.onClick, this, {
6473     single: true,
6474     delay: 100,
6475     stopEvent : true,
6476     forumId: 4
6477 });</code></pre>
6478      * <p>
6479      * <b>Attaching multiple handlers in 1 call</b><br>
6480       * The method also allows for a single argument to be passed which is a config object containing properties
6481      * which specify multiple handlers.
6482      * <p>
6483      * Code:<pre><code>
6484 el.on({
6485     'click' : {
6486         fn: this.onClick
6487         scope: this,
6488         delay: 100
6489     },
6490     'mouseover' : {
6491         fn: this.onMouseOver
6492         scope: this
6493     },
6494     'mouseout' : {
6495         fn: this.onMouseOut
6496         scope: this
6497     }
6498 });</code></pre>
6499      * <p>
6500      * Or a shorthand syntax:<br>
6501      * Code:<pre><code>
6502 el.on({
6503     'click' : this.onClick,
6504     'mouseover' : this.onMouseOver,
6505     'mouseout' : this.onMouseOut
6506     scope: this
6507 });</code></pre>
6508      */
6509         addListener : function(element, eventName, fn, scope, options){
6510             if(typeof eventName == "object"){
6511                 var o = eventName;
6512                 for(var e in o){
6513                     if(propRe.test(e)){
6514                         continue;
6515                     }
6516                     if(typeof o[e] == "function"){
6517                         // shared options
6518                         listen(element, e, o, o[e], o.scope);
6519                     }else{
6520                         // individual options
6521                         listen(element, e, o[e]);
6522                     }
6523                 }
6524                 return;
6525             }
6526             return listen(element, eventName, options, fn, scope);
6527         },
6528         
6529         /**
6530          * Removes an event handler
6531          *
6532          * @param {String/HTMLElement}   element        The id or html element to remove the 
6533          *                             event from
6534          * @param {String}   eventName     The type of event
6535          * @param {Function} fn
6536          * @return {Boolean} True if a listener was actually removed
6537          */
6538         removeListener : function(element, eventName, fn){
6539             return stopListening(element, eventName, fn);
6540         },
6541         
6542         /**
6543          * Fires when the document is ready (before onload and before images are loaded). Can be 
6544          * accessed shorthanded Roo.onReady().
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    An  object that becomes the scope of the handler
6547          * @param {boolean}  options
6548          */
6549         onDocumentReady : function(fn, scope, options){
6550             if(docReadyState){ // if it already fired
6551                 docReadyEvent.addListener(fn, scope, options);
6552                 docReadyEvent.fire();
6553                 docReadyEvent.clearListeners();
6554                 return;
6555             }
6556             if(!docReadyEvent){
6557                 initDocReady();
6558             }
6559             docReadyEvent.addListener(fn, scope, options);
6560         },
6561         
6562         /**
6563          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6564          * @param {Function} fn        The method the event invokes
6565          * @param {Object}   scope    An object that becomes the scope of the handler
6566          * @param {boolean}  options
6567          */
6568         onWindowResize : function(fn, scope, options){
6569             if(!resizeEvent){
6570                 resizeEvent = new Roo.util.Event();
6571                 resizeTask = new Roo.util.DelayedTask(function(){
6572                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6573                 });
6574                 E.on(window, "resize", function(){
6575                     if(Roo.isIE){
6576                         resizeTask.delay(50);
6577                     }else{
6578                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6579                     }
6580                 });
6581             }
6582             resizeEvent.addListener(fn, scope, options);
6583         },
6584
6585         /**
6586          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6587          * @param {Function} fn        The method the event invokes
6588          * @param {Object}   scope    An object that becomes the scope of the handler
6589          * @param {boolean}  options
6590          */
6591         onTextResize : function(fn, scope, options){
6592             if(!textEvent){
6593                 textEvent = new Roo.util.Event();
6594                 var textEl = new Roo.Element(document.createElement('div'));
6595                 textEl.dom.className = 'x-text-resize';
6596                 textEl.dom.innerHTML = 'X';
6597                 textEl.appendTo(document.body);
6598                 textSize = textEl.dom.offsetHeight;
6599                 setInterval(function(){
6600                     if(textEl.dom.offsetHeight != textSize){
6601                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6602                     }
6603                 }, this.textResizeInterval);
6604             }
6605             textEvent.addListener(fn, scope, options);
6606         },
6607
6608         /**
6609          * Removes the passed window resize listener.
6610          * @param {Function} fn        The method the event invokes
6611          * @param {Object}   scope    The scope of handler
6612          */
6613         removeResizeListener : function(fn, scope){
6614             if(resizeEvent){
6615                 resizeEvent.removeListener(fn, scope);
6616             }
6617         },
6618
6619         // private
6620         fireResize : function(){
6621             if(resizeEvent){
6622                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6623             }   
6624         },
6625         /**
6626          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6627          */
6628         ieDeferSrc : false,
6629         /**
6630          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6631          */
6632         textResizeInterval : 50
6633     };
6634     
6635     /**
6636      * Fix for doc tools
6637      * @scopeAlias pub=Roo.EventManager
6638      */
6639     
6640      /**
6641      * Appends an event handler to an element (shorthand for addListener)
6642      * @param {String/HTMLElement}   element        The html element or id to assign the
6643      * @param {String}   eventName The type of event to listen for
6644      * @param {Function} handler The method the event invokes
6645      * @param {Object}   scope (optional) The scope in which to execute the handler
6646      * function. The handler function's "this" context.
6647      * @param {Object}   options (optional) An object containing handler configuration
6648      * properties. This may contain any of the following properties:<ul>
6649      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6650      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6651      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6652      * <li>preventDefault {Boolean} True to prevent the default action</li>
6653      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6654      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6655      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6656      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6657      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6658      * by the specified number of milliseconds. If the event fires again within that time, the original
6659      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6660      * </ul><br>
6661      * <p>
6662      * <b>Combining Options</b><br>
6663      * Using the options argument, it is possible to combine different types of listeners:<br>
6664      * <br>
6665      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6666      * Code:<pre><code>
6667 el.on('click', this.onClick, this, {
6668     single: true,
6669     delay: 100,
6670     stopEvent : true,
6671     forumId: 4
6672 });</code></pre>
6673      * <p>
6674      * <b>Attaching multiple handlers in 1 call</b><br>
6675       * The method also allows for a single argument to be passed which is a config object containing properties
6676      * which specify multiple handlers.
6677      * <p>
6678      * Code:<pre><code>
6679 el.on({
6680     'click' : {
6681         fn: this.onClick
6682         scope: this,
6683         delay: 100
6684     },
6685     'mouseover' : {
6686         fn: this.onMouseOver
6687         scope: this
6688     },
6689     'mouseout' : {
6690         fn: this.onMouseOut
6691         scope: this
6692     }
6693 });</code></pre>
6694      * <p>
6695      * Or a shorthand syntax:<br>
6696      * Code:<pre><code>
6697 el.on({
6698     'click' : this.onClick,
6699     'mouseover' : this.onMouseOver,
6700     'mouseout' : this.onMouseOut
6701     scope: this
6702 });</code></pre>
6703      */
6704     pub.on = pub.addListener;
6705     pub.un = pub.removeListener;
6706
6707     pub.stoppedMouseDownEvent = new Roo.util.Event();
6708     return pub;
6709 }();
6710 /**
6711   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6712   * @param {Function} fn        The method the event invokes
6713   * @param {Object}   scope    An  object that becomes the scope of the handler
6714   * @param {boolean}  override If true, the obj passed in becomes
6715   *                             the execution scope of the listener
6716   * @member Roo
6717   * @method onReady
6718  */
6719 Roo.onReady = Roo.EventManager.onDocumentReady;
6720
6721 Roo.onReady(function(){
6722     var bd = Roo.get(document.body);
6723     if(!bd){ return; }
6724
6725     var cls = [
6726             Roo.isIE ? "roo-ie"
6727             : Roo.isIE11 ? "roo-ie11"
6728             : Roo.isEdge ? "roo-edge"
6729             : Roo.isGecko ? "roo-gecko"
6730             : Roo.isOpera ? "roo-opera"
6731             : Roo.isSafari ? "roo-safari" : ""];
6732
6733     if(Roo.isMac){
6734         cls.push("roo-mac");
6735     }
6736     if(Roo.isLinux){
6737         cls.push("roo-linux");
6738     }
6739     if(Roo.isIOS){
6740         cls.push("roo-ios");
6741     }
6742     if(Roo.isTouch){
6743         cls.push("roo-touch");
6744     }
6745     if(Roo.isBorderBox){
6746         cls.push('roo-border-box');
6747     }
6748     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6749         var p = bd.dom.parentNode;
6750         if(p){
6751             p.className += ' roo-strict';
6752         }
6753     }
6754     bd.addClass(cls.join(' '));
6755 });
6756
6757 /**
6758  * @class Roo.EventObject
6759  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6760  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6761  * Example:
6762  * <pre><code>
6763  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6764     e.preventDefault();
6765     var target = e.getTarget();
6766     ...
6767  }
6768  var myDiv = Roo.get("myDiv");
6769  myDiv.on("click", handleClick);
6770  //or
6771  Roo.EventManager.on("myDiv", 'click', handleClick);
6772  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6773  </code></pre>
6774  * @singleton
6775  */
6776 Roo.EventObject = function(){
6777     
6778     var E = Roo.lib.Event;
6779     
6780     // safari keypress events for special keys return bad keycodes
6781     var safariKeys = {
6782         63234 : 37, // left
6783         63235 : 39, // right
6784         63232 : 38, // up
6785         63233 : 40, // down
6786         63276 : 33, // page up
6787         63277 : 34, // page down
6788         63272 : 46, // delete
6789         63273 : 36, // home
6790         63275 : 35  // end
6791     };
6792
6793     // normalize button clicks
6794     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6795                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6796
6797     Roo.EventObjectImpl = function(e){
6798         if(e){
6799             this.setEvent(e.browserEvent || e);
6800         }
6801     };
6802     Roo.EventObjectImpl.prototype = {
6803         /**
6804          * Used to fix doc tools.
6805          * @scope Roo.EventObject.prototype
6806          */
6807             
6808
6809         
6810         
6811         /** The normal browser event */
6812         browserEvent : null,
6813         /** The button pressed in a mouse event */
6814         button : -1,
6815         /** True if the shift key was down during the event */
6816         shiftKey : false,
6817         /** True if the control key was down during the event */
6818         ctrlKey : false,
6819         /** True if the alt key was down during the event */
6820         altKey : false,
6821
6822         /** Key constant 
6823         * @type Number */
6824         BACKSPACE : 8,
6825         /** Key constant 
6826         * @type Number */
6827         TAB : 9,
6828         /** Key constant 
6829         * @type Number */
6830         RETURN : 13,
6831         /** Key constant 
6832         * @type Number */
6833         ENTER : 13,
6834         /** Key constant 
6835         * @type Number */
6836         SHIFT : 16,
6837         /** Key constant 
6838         * @type Number */
6839         CONTROL : 17,
6840         /** Key constant 
6841         * @type Number */
6842         ESC : 27,
6843         /** Key constant 
6844         * @type Number */
6845         SPACE : 32,
6846         /** Key constant 
6847         * @type Number */
6848         PAGEUP : 33,
6849         /** Key constant 
6850         * @type Number */
6851         PAGEDOWN : 34,
6852         /** Key constant 
6853         * @type Number */
6854         END : 35,
6855         /** Key constant 
6856         * @type Number */
6857         HOME : 36,
6858         /** Key constant 
6859         * @type Number */
6860         LEFT : 37,
6861         /** Key constant 
6862         * @type Number */
6863         UP : 38,
6864         /** Key constant 
6865         * @type Number */
6866         RIGHT : 39,
6867         /** Key constant 
6868         * @type Number */
6869         DOWN : 40,
6870         /** Key constant 
6871         * @type Number */
6872         DELETE : 46,
6873         /** Key constant 
6874         * @type Number */
6875         F5 : 116,
6876
6877            /** @private */
6878         setEvent : function(e){
6879             if(e == this || (e && e.browserEvent)){ // already wrapped
6880                 return e;
6881             }
6882             this.browserEvent = e;
6883             if(e){
6884                 // normalize buttons
6885                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6886                 if(e.type == 'click' && this.button == -1){
6887                     this.button = 0;
6888                 }
6889                 this.type = e.type;
6890                 this.shiftKey = e.shiftKey;
6891                 // mac metaKey behaves like ctrlKey
6892                 this.ctrlKey = e.ctrlKey || e.metaKey;
6893                 this.altKey = e.altKey;
6894                 // in getKey these will be normalized for the mac
6895                 this.keyCode = e.keyCode;
6896                 // keyup warnings on firefox.
6897                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6898                 // cache the target for the delayed and or buffered events
6899                 this.target = E.getTarget(e);
6900                 // same for XY
6901                 this.xy = E.getXY(e);
6902             }else{
6903                 this.button = -1;
6904                 this.shiftKey = false;
6905                 this.ctrlKey = false;
6906                 this.altKey = false;
6907                 this.keyCode = 0;
6908                 this.charCode =0;
6909                 this.target = null;
6910                 this.xy = [0, 0];
6911             }
6912             return this;
6913         },
6914
6915         /**
6916          * Stop the event (preventDefault and stopPropagation)
6917          */
6918         stopEvent : function(){
6919             if(this.browserEvent){
6920                 if(this.browserEvent.type == 'mousedown'){
6921                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6922                 }
6923                 E.stopEvent(this.browserEvent);
6924             }
6925         },
6926
6927         /**
6928          * Prevents the browsers default handling of the event.
6929          */
6930         preventDefault : function(){
6931             if(this.browserEvent){
6932                 E.preventDefault(this.browserEvent);
6933             }
6934         },
6935
6936         /** @private */
6937         isNavKeyPress : function(){
6938             var k = this.keyCode;
6939             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6940             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6941         },
6942
6943         isSpecialKey : function(){
6944             var k = this.keyCode;
6945             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6946             (k == 16) || (k == 17) ||
6947             (k >= 18 && k <= 20) ||
6948             (k >= 33 && k <= 35) ||
6949             (k >= 36 && k <= 39) ||
6950             (k >= 44 && k <= 45);
6951         },
6952         /**
6953          * Cancels bubbling of the event.
6954          */
6955         stopPropagation : function(){
6956             if(this.browserEvent){
6957                 if(this.type == 'mousedown'){
6958                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6959                 }
6960                 E.stopPropagation(this.browserEvent);
6961             }
6962         },
6963
6964         /**
6965          * Gets the key code for the event.
6966          * @return {Number}
6967          */
6968         getCharCode : function(){
6969             return this.charCode || this.keyCode;
6970         },
6971
6972         /**
6973          * Returns a normalized keyCode for the event.
6974          * @return {Number} The key code
6975          */
6976         getKey : function(){
6977             var k = this.keyCode || this.charCode;
6978             return Roo.isSafari ? (safariKeys[k] || k) : k;
6979         },
6980
6981         /**
6982          * Gets the x coordinate of the event.
6983          * @return {Number}
6984          */
6985         getPageX : function(){
6986             return this.xy[0];
6987         },
6988
6989         /**
6990          * Gets the y coordinate of the event.
6991          * @return {Number}
6992          */
6993         getPageY : function(){
6994             return this.xy[1];
6995         },
6996
6997         /**
6998          * Gets the time of the event.
6999          * @return {Number}
7000          */
7001         getTime : function(){
7002             if(this.browserEvent){
7003                 return E.getTime(this.browserEvent);
7004             }
7005             return null;
7006         },
7007
7008         /**
7009          * Gets the page coordinates of the event.
7010          * @return {Array} The xy values like [x, y]
7011          */
7012         getXY : function(){
7013             return this.xy;
7014         },
7015
7016         /**
7017          * Gets the target for the event.
7018          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7019          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7020                 search as a number or element (defaults to 10 || document.body)
7021          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7022          * @return {HTMLelement}
7023          */
7024         getTarget : function(selector, maxDepth, returnEl){
7025             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7026         },
7027         /**
7028          * Gets the related target.
7029          * @return {HTMLElement}
7030          */
7031         getRelatedTarget : function(){
7032             if(this.browserEvent){
7033                 return E.getRelatedTarget(this.browserEvent);
7034             }
7035             return null;
7036         },
7037
7038         /**
7039          * Normalizes mouse wheel delta across browsers
7040          * @return {Number} The delta
7041          */
7042         getWheelDelta : function(){
7043             var e = this.browserEvent;
7044             var delta = 0;
7045             if(e.wheelDelta){ /* IE/Opera. */
7046                 delta = e.wheelDelta/120;
7047             }else if(e.detail){ /* Mozilla case. */
7048                 delta = -e.detail/3;
7049             }
7050             return delta;
7051         },
7052
7053         /**
7054          * Returns true if the control, meta, shift or alt key was pressed during this event.
7055          * @return {Boolean}
7056          */
7057         hasModifier : function(){
7058             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7059         },
7060
7061         /**
7062          * Returns true if the target of this event equals el or is a child of el
7063          * @param {String/HTMLElement/Element} el
7064          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7065          * @return {Boolean}
7066          */
7067         within : function(el, related){
7068             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7069             return t && Roo.fly(el).contains(t);
7070         },
7071
7072         getPoint : function(){
7073             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7074         }
7075     };
7076
7077     return new Roo.EventObjectImpl();
7078 }();
7079             
7080     /*
7081  * Based on:
7082  * Ext JS Library 1.1.1
7083  * Copyright(c) 2006-2007, Ext JS, LLC.
7084  *
7085  * Originally Released Under LGPL - original licence link has changed is not relivant.
7086  *
7087  * Fork - LGPL
7088  * <script type="text/javascript">
7089  */
7090
7091  
7092 // was in Composite Element!??!?!
7093  
7094 (function(){
7095     var D = Roo.lib.Dom;
7096     var E = Roo.lib.Event;
7097     var A = Roo.lib.Anim;
7098
7099     // local style camelizing for speed
7100     var propCache = {};
7101     var camelRe = /(-[a-z])/gi;
7102     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7103     var view = document.defaultView;
7104
7105 /**
7106  * @class Roo.Element
7107  * Represents an Element in the DOM.<br><br>
7108  * Usage:<br>
7109 <pre><code>
7110 var el = Roo.get("my-div");
7111
7112 // or with getEl
7113 var el = getEl("my-div");
7114
7115 // or with a DOM element
7116 var el = Roo.get(myDivElement);
7117 </code></pre>
7118  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7119  * each call instead of constructing a new one.<br><br>
7120  * <b>Animations</b><br />
7121  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7122  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7123 <pre>
7124 Option    Default   Description
7125 --------- --------  ---------------------------------------------
7126 duration  .35       The duration of the animation in seconds
7127 easing    easeOut   The YUI easing method
7128 callback  none      A function to execute when the anim completes
7129 scope     this      The scope (this) of the callback function
7130 </pre>
7131 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7132 * manipulate the animation. Here's an example:
7133 <pre><code>
7134 var el = Roo.get("my-div");
7135
7136 // no animation
7137 el.setWidth(100);
7138
7139 // default animation
7140 el.setWidth(100, true);
7141
7142 // animation with some options set
7143 el.setWidth(100, {
7144     duration: 1,
7145     callback: this.foo,
7146     scope: this
7147 });
7148
7149 // using the "anim" property to get the Anim object
7150 var opt = {
7151     duration: 1,
7152     callback: this.foo,
7153     scope: this
7154 };
7155 el.setWidth(100, opt);
7156 ...
7157 if(opt.anim.isAnimated()){
7158     opt.anim.stop();
7159 }
7160 </code></pre>
7161 * <b> Composite (Collections of) Elements</b><br />
7162  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7163  * @constructor Create a new Element directly.
7164  * @param {String/HTMLElement} element
7165  * @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).
7166  */
7167     Roo.Element = function(element, forceNew)
7168     {
7169         var dom = typeof element == "string" ?
7170                 document.getElementById(element) : element;
7171         
7172         this.listeners = {};
7173         
7174         if(!dom){ // invalid id/element
7175             return null;
7176         }
7177         var id = dom.id;
7178         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7179             return Roo.Element.cache[id];
7180         }
7181
7182         /**
7183          * The DOM element
7184          * @type HTMLElement
7185          */
7186         this.dom = dom;
7187
7188         /**
7189          * The DOM element ID
7190          * @type String
7191          */
7192         this.id = id || Roo.id(dom);
7193         
7194         return this; // assumed for cctor?
7195     };
7196
7197     var El = Roo.Element;
7198
7199     El.prototype = {
7200         /**
7201          * The element's default display mode  (defaults to "") 
7202          * @type String
7203          */
7204         originalDisplay : "",
7205
7206         
7207         // note this is overridden in BS version..
7208         visibilityMode : 1, 
7209         /**
7210          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7211          * @type String
7212          */
7213         defaultUnit : "px",
7214         
7215         /**
7216          * Sets the element's visibility mode. When setVisible() is called it
7217          * will use this to determine whether to set the visibility or the display property.
7218          * @param visMode Element.VISIBILITY or Element.DISPLAY
7219          * @return {Roo.Element} this
7220          */
7221         setVisibilityMode : function(visMode){
7222             this.visibilityMode = visMode;
7223             return this;
7224         },
7225         /**
7226          * Convenience method for setVisibilityMode(Element.DISPLAY)
7227          * @param {String} display (optional) What to set display to when visible
7228          * @return {Roo.Element} this
7229          */
7230         enableDisplayMode : function(display){
7231             this.setVisibilityMode(El.DISPLAY);
7232             if(typeof display != "undefined") { this.originalDisplay = display; }
7233             return this;
7234         },
7235
7236         /**
7237          * 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)
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7242          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7243          */
7244         findParent : function(simpleSelector, maxDepth, returnEl){
7245             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7246             maxDepth = maxDepth || 50;
7247             if(typeof maxDepth != "number"){
7248                 stopEl = Roo.getDom(maxDepth);
7249                 maxDepth = 10;
7250             }
7251             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7252                 if(dq.is(p, simpleSelector)){
7253                     return returnEl ? Roo.get(p) : p;
7254                 }
7255                 depth++;
7256                 p = p.parentNode;
7257             }
7258             return null;
7259         },
7260
7261
7262         /**
7263          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7264          * @param {String} selector The simple selector to test
7265          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7266                 search as a number or element (defaults to 10 || document.body)
7267          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7268          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7269          */
7270         findParentNode : function(simpleSelector, maxDepth, returnEl){
7271             var p = Roo.fly(this.dom.parentNode, '_internal');
7272             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7273         },
7274         
7275         /**
7276          * Looks at  the scrollable parent element
7277          */
7278         findScrollableParent : function()
7279         {
7280             var overflowRegex = /(auto|scroll)/;
7281             
7282             if(this.getStyle('position') === 'fixed'){
7283                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7284             }
7285             
7286             var excludeStaticParent = this.getStyle('position') === "absolute";
7287             
7288             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7289                 
7290                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7291                     continue;
7292                 }
7293                 
7294                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7295                     return parent;
7296                 }
7297                 
7298                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7299                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7300                 }
7301             }
7302             
7303             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7304         },
7305
7306         /**
7307          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7308          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7309          * @param {String} selector The simple selector to test
7310          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7311                 search as a number or element (defaults to 10 || document.body)
7312          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7313          */
7314         up : function(simpleSelector, maxDepth){
7315             return this.findParentNode(simpleSelector, maxDepth, true);
7316         },
7317
7318
7319
7320         /**
7321          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7322          * @param {String} selector The simple selector to test
7323          * @return {Boolean} True if this element matches the selector, else false
7324          */
7325         is : function(simpleSelector){
7326             return Roo.DomQuery.is(this.dom, simpleSelector);
7327         },
7328
7329         /**
7330          * Perform animation on this element.
7331          * @param {Object} args The YUI animation control args
7332          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7333          * @param {Function} onComplete (optional) Function to call when animation completes
7334          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7335          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7336          * @return {Roo.Element} this
7337          */
7338         animate : function(args, duration, onComplete, easing, animType){
7339             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7340             return this;
7341         },
7342
7343         /*
7344          * @private Internal animation call
7345          */
7346         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7347             animType = animType || 'run';
7348             opt = opt || {};
7349             var anim = Roo.lib.Anim[animType](
7350                 this.dom, args,
7351                 (opt.duration || defaultDur) || .35,
7352                 (opt.easing || defaultEase) || 'easeOut',
7353                 function(){
7354                     Roo.callback(cb, this);
7355                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7356                 },
7357                 this
7358             );
7359             opt.anim = anim;
7360             return anim;
7361         },
7362
7363         // private legacy anim prep
7364         preanim : function(a, i){
7365             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7366         },
7367
7368         /**
7369          * Removes worthless text nodes
7370          * @param {Boolean} forceReclean (optional) By default the element
7371          * keeps track if it has been cleaned already so
7372          * you can call this over and over. However, if you update the element and
7373          * need to force a reclean, you can pass true.
7374          */
7375         clean : function(forceReclean){
7376             if(this.isCleaned && forceReclean !== true){
7377                 return this;
7378             }
7379             var ns = /\S/;
7380             var d = this.dom, n = d.firstChild, ni = -1;
7381             while(n){
7382                 var nx = n.nextSibling;
7383                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7384                     d.removeChild(n);
7385                 }else{
7386                     n.nodeIndex = ++ni;
7387                 }
7388                 n = nx;
7389             }
7390             this.isCleaned = true;
7391             return this;
7392         },
7393
7394         // private
7395         calcOffsetsTo : function(el){
7396             el = Roo.get(el);
7397             var d = el.dom;
7398             var restorePos = false;
7399             if(el.getStyle('position') == 'static'){
7400                 el.position('relative');
7401                 restorePos = true;
7402             }
7403             var x = 0, y =0;
7404             var op = this.dom;
7405             while(op && op != d && op.tagName != 'HTML'){
7406                 x+= op.offsetLeft;
7407                 y+= op.offsetTop;
7408                 op = op.offsetParent;
7409             }
7410             if(restorePos){
7411                 el.position('static');
7412             }
7413             return [x, y];
7414         },
7415
7416         /**
7417          * Scrolls this element into view within the passed container.
7418          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7419          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7420          * @return {Roo.Element} this
7421          */
7422         scrollIntoView : function(container, hscroll){
7423             var c = Roo.getDom(container) || document.body;
7424             var el = this.dom;
7425
7426             var o = this.calcOffsetsTo(c),
7427                 l = o[0],
7428                 t = o[1],
7429                 b = t+el.offsetHeight,
7430                 r = l+el.offsetWidth;
7431
7432             var ch = c.clientHeight;
7433             var ct = parseInt(c.scrollTop, 10);
7434             var cl = parseInt(c.scrollLeft, 10);
7435             var cb = ct + ch;
7436             var cr = cl + c.clientWidth;
7437
7438             if(t < ct){
7439                 c.scrollTop = t;
7440             }else if(b > cb){
7441                 c.scrollTop = b-ch;
7442             }
7443
7444             if(hscroll !== false){
7445                 if(l < cl){
7446                     c.scrollLeft = l;
7447                 }else if(r > cr){
7448                     c.scrollLeft = r-c.clientWidth;
7449                 }
7450             }
7451             return this;
7452         },
7453
7454         // private
7455         scrollChildIntoView : function(child, hscroll){
7456             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7457         },
7458
7459         /**
7460          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7461          * the new height may not be available immediately.
7462          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7463          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7464          * @param {Function} onComplete (optional) Function to call when animation completes
7465          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7466          * @return {Roo.Element} this
7467          */
7468         autoHeight : function(animate, duration, onComplete, easing){
7469             var oldHeight = this.getHeight();
7470             this.clip();
7471             this.setHeight(1); // force clipping
7472             setTimeout(function(){
7473                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7474                 if(!animate){
7475                     this.setHeight(height);
7476                     this.unclip();
7477                     if(typeof onComplete == "function"){
7478                         onComplete();
7479                     }
7480                 }else{
7481                     this.setHeight(oldHeight); // restore original height
7482                     this.setHeight(height, animate, duration, function(){
7483                         this.unclip();
7484                         if(typeof onComplete == "function") { onComplete(); }
7485                     }.createDelegate(this), easing);
7486                 }
7487             }.createDelegate(this), 0);
7488             return this;
7489         },
7490
7491         /**
7492          * Returns true if this element is an ancestor of the passed element
7493          * @param {HTMLElement/String} el The element to check
7494          * @return {Boolean} True if this element is an ancestor of el, else false
7495          */
7496         contains : function(el){
7497             if(!el){return false;}
7498             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7499         },
7500
7501         /**
7502          * Checks whether the element is currently visible using both visibility and display properties.
7503          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7504          * @return {Boolean} True if the element is currently visible, else false
7505          */
7506         isVisible : function(deep) {
7507             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7508             if(deep !== true || !vis){
7509                 return vis;
7510             }
7511             var p = this.dom.parentNode;
7512             while(p && p.tagName.toLowerCase() != "body"){
7513                 if(!Roo.fly(p, '_isVisible').isVisible()){
7514                     return false;
7515                 }
7516                 p = p.parentNode;
7517             }
7518             return true;
7519         },
7520
7521         /**
7522          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7523          * @param {String} selector The CSS selector
7524          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7525          * @return {CompositeElement/CompositeElementLite} The composite element
7526          */
7527         select : function(selector, unique){
7528             return El.select(selector, unique, this.dom);
7529         },
7530
7531         /**
7532          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7533          * @param {String} selector The CSS selector
7534          * @return {Array} An array of the matched nodes
7535          */
7536         query : function(selector, unique){
7537             return Roo.DomQuery.select(selector, this.dom);
7538         },
7539
7540         /**
7541          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7542          * @param {String} selector The CSS selector
7543          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7544          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7545          */
7546         child : function(selector, returnDom){
7547             var n = Roo.DomQuery.selectNode(selector, this.dom);
7548             return returnDom ? n : Roo.get(n);
7549         },
7550
7551         /**
7552          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7553          * @param {String} selector The CSS selector
7554          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7555          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7556          */
7557         down : function(selector, returnDom){
7558             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7559             return returnDom ? n : Roo.get(n);
7560         },
7561
7562         /**
7563          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7564          * @param {String} group The group the DD object is member of
7565          * @param {Object} config The DD config object
7566          * @param {Object} overrides An object containing methods to override/implement on the DD object
7567          * @return {Roo.dd.DD} The DD object
7568          */
7569         initDD : function(group, config, overrides){
7570             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7571             return Roo.apply(dd, overrides);
7572         },
7573
7574         /**
7575          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7576          * @param {String} group The group the DDProxy object is member of
7577          * @param {Object} config The DDProxy config object
7578          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7579          * @return {Roo.dd.DDProxy} The DDProxy object
7580          */
7581         initDDProxy : function(group, config, overrides){
7582             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7583             return Roo.apply(dd, overrides);
7584         },
7585
7586         /**
7587          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7588          * @param {String} group The group the DDTarget object is member of
7589          * @param {Object} config The DDTarget config object
7590          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7591          * @return {Roo.dd.DDTarget} The DDTarget object
7592          */
7593         initDDTarget : function(group, config, overrides){
7594             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7595             return Roo.apply(dd, overrides);
7596         },
7597
7598         /**
7599          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7600          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7601          * @param {Boolean} visible Whether the element is visible
7602          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7603          * @return {Roo.Element} this
7604          */
7605          setVisible : function(visible, animate){
7606             if(!animate || !A){
7607                 if(this.visibilityMode == El.DISPLAY){
7608                     this.setDisplayed(visible);
7609                 }else{
7610                     this.fixDisplay();
7611                     this.dom.style.visibility = visible ? "visible" : "hidden";
7612                 }
7613             }else{
7614                 // closure for composites
7615                 var dom = this.dom;
7616                 var visMode = this.visibilityMode;
7617                 if(visible){
7618                     this.setOpacity(.01);
7619                     this.setVisible(true);
7620                 }
7621                 this.anim({opacity: { to: (visible?1:0) }},
7622                       this.preanim(arguments, 1),
7623                       null, .35, 'easeIn', function(){
7624                          if(!visible){
7625                              if(visMode == El.DISPLAY){
7626                                  dom.style.display = "none";
7627                              }else{
7628                                  dom.style.visibility = "hidden";
7629                              }
7630                              Roo.get(dom).setOpacity(1);
7631                          }
7632                      });
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Returns true if display is not "none"
7639          * @return {Boolean}
7640          */
7641         isDisplayed : function() {
7642             return this.getStyle("display") != "none";
7643         },
7644
7645         /**
7646          * Toggles the element's visibility or display, depending on visibility mode.
7647          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7648          * @return {Roo.Element} this
7649          */
7650         toggle : function(animate){
7651             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7652             return this;
7653         },
7654
7655         /**
7656          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7657          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7658          * @return {Roo.Element} this
7659          */
7660         setDisplayed : function(value) {
7661             if(typeof value == "boolean"){
7662                value = value ? this.originalDisplay : "none";
7663             }
7664             this.setStyle("display", value);
7665             return this;
7666         },
7667
7668         /**
7669          * Tries to focus the element. Any exceptions are caught and ignored.
7670          * @return {Roo.Element} this
7671          */
7672         focus : function() {
7673             try{
7674                 this.dom.focus();
7675             }catch(e){}
7676             return this;
7677         },
7678
7679         /**
7680          * Tries to blur the element. Any exceptions are caught and ignored.
7681          * @return {Roo.Element} this
7682          */
7683         blur : function() {
7684             try{
7685                 this.dom.blur();
7686             }catch(e){}
7687             return this;
7688         },
7689
7690         /**
7691          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7692          * @param {String/Array} className The CSS class to add, or an array of classes
7693          * @return {Roo.Element} this
7694          */
7695         addClass : function(className){
7696             if(className instanceof Array){
7697                 for(var i = 0, len = className.length; i < len; i++) {
7698                     this.addClass(className[i]);
7699                 }
7700             }else{
7701                 if(className && !this.hasClass(className)){
7702                     if (this.dom instanceof SVGElement) {
7703                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7704                     } else {
7705                         this.dom.className = this.dom.className + " " + className;
7706                     }
7707                 }
7708             }
7709             return this;
7710         },
7711
7712         /**
7713          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7714          * @param {String/Array} className The CSS class to add, or an array of classes
7715          * @return {Roo.Element} this
7716          */
7717         radioClass : function(className){
7718             var siblings = this.dom.parentNode.childNodes;
7719             for(var i = 0; i < siblings.length; i++) {
7720                 var s = siblings[i];
7721                 if(s.nodeType == 1){
7722                     Roo.get(s).removeClass(className);
7723                 }
7724             }
7725             this.addClass(className);
7726             return this;
7727         },
7728
7729         /**
7730          * Removes one or more CSS classes from the element.
7731          * @param {String/Array} className The CSS class to remove, or an array of classes
7732          * @return {Roo.Element} this
7733          */
7734         removeClass : function(className){
7735             
7736             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7737             if(!className || !cn){
7738                 return this;
7739             }
7740             if(className instanceof Array){
7741                 for(var i = 0, len = className.length; i < len; i++) {
7742                     this.removeClass(className[i]);
7743                 }
7744             }else{
7745                 if(this.hasClass(className)){
7746                     var re = this.classReCache[className];
7747                     if (!re) {
7748                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7749                        this.classReCache[className] = re;
7750                     }
7751                     if (this.dom instanceof SVGElement) {
7752                         this.dom.className.baseVal = cn.replace(re, " ");
7753                     } else {
7754                         this.dom.className = cn.replace(re, " ");
7755                     }
7756                 }
7757             }
7758             return this;
7759         },
7760
7761         // private
7762         classReCache: {},
7763
7764         /**
7765          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7766          * @param {String} className The CSS class to toggle
7767          * @return {Roo.Element} this
7768          */
7769         toggleClass : function(className){
7770             if(this.hasClass(className)){
7771                 this.removeClass(className);
7772             }else{
7773                 this.addClass(className);
7774             }
7775             return this;
7776         },
7777
7778         /**
7779          * Checks if the specified CSS class exists on this element's DOM node.
7780          * @param {String} className The CSS class to check for
7781          * @return {Boolean} True if the class exists, else false
7782          */
7783         hasClass : function(className){
7784             if (this.dom instanceof SVGElement) {
7785                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7786             } 
7787             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7788         },
7789
7790         /**
7791          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7792          * @param {String} oldClassName The CSS class to replace
7793          * @param {String} newClassName The replacement CSS class
7794          * @return {Roo.Element} this
7795          */
7796         replaceClass : function(oldClassName, newClassName){
7797             this.removeClass(oldClassName);
7798             this.addClass(newClassName);
7799             return this;
7800         },
7801
7802         /**
7803          * Returns an object with properties matching the styles requested.
7804          * For example, el.getStyles('color', 'font-size', 'width') might return
7805          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7806          * @param {String} style1 A style name
7807          * @param {String} style2 A style name
7808          * @param {String} etc.
7809          * @return {Object} The style object
7810          */
7811         getStyles : function(){
7812             var a = arguments, len = a.length, r = {};
7813             for(var i = 0; i < len; i++){
7814                 r[a[i]] = this.getStyle(a[i]);
7815             }
7816             return r;
7817         },
7818
7819         /**
7820          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7821          * @param {String} property The style property whose value is returned.
7822          * @return {String} The current value of the style property for this element.
7823          */
7824         getStyle : function(){
7825             return view && view.getComputedStyle ?
7826                 function(prop){
7827                     var el = this.dom, v, cs, camel;
7828                     if(prop == 'float'){
7829                         prop = "cssFloat";
7830                     }
7831                     if(el.style && (v = el.style[prop])){
7832                         return v;
7833                     }
7834                     if(cs = view.getComputedStyle(el, "")){
7835                         if(!(camel = propCache[prop])){
7836                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7837                         }
7838                         return cs[camel];
7839                     }
7840                     return null;
7841                 } :
7842                 function(prop){
7843                     var el = this.dom, v, cs, camel;
7844                     if(prop == 'opacity'){
7845                         if(typeof el.style.filter == 'string'){
7846                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7847                             if(m){
7848                                 var fv = parseFloat(m[1]);
7849                                 if(!isNaN(fv)){
7850                                     return fv ? fv / 100 : 0;
7851                                 }
7852                             }
7853                         }
7854                         return 1;
7855                     }else if(prop == 'float'){
7856                         prop = "styleFloat";
7857                     }
7858                     if(!(camel = propCache[prop])){
7859                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7860                     }
7861                     if(v = el.style[camel]){
7862                         return v;
7863                     }
7864                     if(cs = el.currentStyle){
7865                         return cs[camel];
7866                     }
7867                     return null;
7868                 };
7869         }(),
7870
7871         /**
7872          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7873          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7874          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7875          * @return {Roo.Element} this
7876          */
7877         setStyle : function(prop, value){
7878             if(typeof prop == "string"){
7879                 
7880                 if (prop == 'float') {
7881                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7882                     return this;
7883                 }
7884                 
7885                 var camel;
7886                 if(!(camel = propCache[prop])){
7887                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7888                 }
7889                 
7890                 if(camel == 'opacity') {
7891                     this.setOpacity(value);
7892                 }else{
7893                     this.dom.style[camel] = value;
7894                 }
7895             }else{
7896                 for(var style in prop){
7897                     if(typeof prop[style] != "function"){
7898                        this.setStyle(style, prop[style]);
7899                     }
7900                 }
7901             }
7902             return this;
7903         },
7904
7905         /**
7906          * More flexible version of {@link #setStyle} for setting style properties.
7907          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7908          * a function which returns such a specification.
7909          * @return {Roo.Element} this
7910          */
7911         applyStyles : function(style){
7912             Roo.DomHelper.applyStyles(this.dom, style);
7913             return this;
7914         },
7915
7916         /**
7917           * 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).
7918           * @return {Number} The X position of the element
7919           */
7920         getX : function(){
7921             return D.getX(this.dom);
7922         },
7923
7924         /**
7925           * 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).
7926           * @return {Number} The Y position of the element
7927           */
7928         getY : function(){
7929             return D.getY(this.dom);
7930         },
7931
7932         /**
7933           * 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).
7934           * @return {Array} The XY position of the element
7935           */
7936         getXY : function(){
7937             return D.getXY(this.dom);
7938         },
7939
7940         /**
7941          * 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).
7942          * @param {Number} The X position of the element
7943          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setX : function(x, animate){
7947             if(!animate || !A){
7948                 D.setX(this.dom, x);
7949             }else{
7950                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7951             }
7952             return this;
7953         },
7954
7955         /**
7956          * 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).
7957          * @param {Number} The Y position of the element
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         setY : function(y, animate){
7962             if(!animate || !A){
7963                 D.setY(this.dom, y);
7964             }else{
7965                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7966             }
7967             return this;
7968         },
7969
7970         /**
7971          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7972          * @param {String} left The left CSS property value
7973          * @return {Roo.Element} this
7974          */
7975         setLeft : function(left){
7976             this.setStyle("left", this.addUnits(left));
7977             return this;
7978         },
7979
7980         /**
7981          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7982          * @param {String} top The top CSS property value
7983          * @return {Roo.Element} this
7984          */
7985         setTop : function(top){
7986             this.setStyle("top", this.addUnits(top));
7987             return this;
7988         },
7989
7990         /**
7991          * Sets the element's CSS right style.
7992          * @param {String} right The right CSS property value
7993          * @return {Roo.Element} this
7994          */
7995         setRight : function(right){
7996             this.setStyle("right", this.addUnits(right));
7997             return this;
7998         },
7999
8000         /**
8001          * Sets the element's CSS bottom style.
8002          * @param {String} bottom The bottom CSS property value
8003          * @return {Roo.Element} this
8004          */
8005         setBottom : function(bottom){
8006             this.setStyle("bottom", this.addUnits(bottom));
8007             return this;
8008         },
8009
8010         /**
8011          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8012          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8013          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8014          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8015          * @return {Roo.Element} this
8016          */
8017         setXY : function(pos, animate){
8018             if(!animate || !A){
8019                 D.setXY(this.dom, pos);
8020             }else{
8021                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8022             }
8023             return this;
8024         },
8025
8026         /**
8027          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8028          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8029          * @param {Number} x X value for new position (coordinates are page-based)
8030          * @param {Number} y Y value for new position (coordinates are page-based)
8031          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8032          * @return {Roo.Element} this
8033          */
8034         setLocation : function(x, y, animate){
8035             this.setXY([x, y], this.preanim(arguments, 2));
8036             return this;
8037         },
8038
8039         /**
8040          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8041          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8042          * @param {Number} x X value for new position (coordinates are page-based)
8043          * @param {Number} y Y value for new position (coordinates are page-based)
8044          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8045          * @return {Roo.Element} this
8046          */
8047         moveTo : function(x, y, animate){
8048             this.setXY([x, y], this.preanim(arguments, 2));
8049             return this;
8050         },
8051
8052         /**
8053          * Returns the region of the given element.
8054          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8055          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8056          */
8057         getRegion : function(){
8058             return D.getRegion(this.dom);
8059         },
8060
8061         /**
8062          * Returns the offset height of the element
8063          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8064          * @return {Number} The element's height
8065          */
8066         getHeight : function(contentHeight){
8067             var h = this.dom.offsetHeight || 0;
8068             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8069         },
8070
8071         /**
8072          * Returns the offset width of the element
8073          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8074          * @return {Number} The element's width
8075          */
8076         getWidth : function(contentWidth){
8077             var w = this.dom.offsetWidth || 0;
8078             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8079         },
8080
8081         /**
8082          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8083          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8084          * if a height has not been set using CSS.
8085          * @return {Number}
8086          */
8087         getComputedHeight : function(){
8088             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8089             if(!h){
8090                 h = parseInt(this.getStyle('height'), 10) || 0;
8091                 if(!this.isBorderBox()){
8092                     h += this.getFrameWidth('tb');
8093                 }
8094             }
8095             return h;
8096         },
8097
8098         /**
8099          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8100          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8101          * if a width has not been set using CSS.
8102          * @return {Number}
8103          */
8104         getComputedWidth : function(){
8105             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8106             if(!w){
8107                 w = parseInt(this.getStyle('width'), 10) || 0;
8108                 if(!this.isBorderBox()){
8109                     w += this.getFrameWidth('lr');
8110                 }
8111             }
8112             return w;
8113         },
8114
8115         /**
8116          * Returns the size of the element.
8117          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8118          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8119          */
8120         getSize : function(contentSize){
8121             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8122         },
8123
8124         /**
8125          * Returns the width and height of the viewport.
8126          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8127          */
8128         getViewSize : function(){
8129             var d = this.dom, doc = document, aw = 0, ah = 0;
8130             if(d == doc || d == doc.body){
8131                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8132             }else{
8133                 return {
8134                     width : d.clientWidth,
8135                     height: d.clientHeight
8136                 };
8137             }
8138         },
8139
8140         /**
8141          * Returns the value of the "value" attribute
8142          * @param {Boolean} asNumber true to parse the value as a number
8143          * @return {String/Number}
8144          */
8145         getValue : function(asNumber){
8146             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8147         },
8148
8149         // private
8150         adjustWidth : function(width){
8151             if(typeof width == "number"){
8152                 if(this.autoBoxAdjust && !this.isBorderBox()){
8153                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8154                 }
8155                 if(width < 0){
8156                     width = 0;
8157                 }
8158             }
8159             return width;
8160         },
8161
8162         // private
8163         adjustHeight : function(height){
8164             if(typeof height == "number"){
8165                if(this.autoBoxAdjust && !this.isBorderBox()){
8166                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8167                }
8168                if(height < 0){
8169                    height = 0;
8170                }
8171             }
8172             return height;
8173         },
8174
8175         /**
8176          * Set the width of the element
8177          * @param {Number} width The new width
8178          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8179          * @return {Roo.Element} this
8180          */
8181         setWidth : function(width, animate){
8182             width = this.adjustWidth(width);
8183             if(!animate || !A){
8184                 this.dom.style.width = this.addUnits(width);
8185             }else{
8186                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8187             }
8188             return this;
8189         },
8190
8191         /**
8192          * Set the height of the element
8193          * @param {Number} height The new height
8194          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8195          * @return {Roo.Element} this
8196          */
8197          setHeight : function(height, animate){
8198             height = this.adjustHeight(height);
8199             if(!animate || !A){
8200                 this.dom.style.height = this.addUnits(height);
8201             }else{
8202                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8203             }
8204             return this;
8205         },
8206
8207         /**
8208          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8209          * @param {Number} width The new width
8210          * @param {Number} height The new height
8211          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8212          * @return {Roo.Element} this
8213          */
8214          setSize : function(width, height, animate){
8215             if(typeof width == "object"){ // in case of object from getSize()
8216                 height = width.height; width = width.width;
8217             }
8218             width = this.adjustWidth(width); height = this.adjustHeight(height);
8219             if(!animate || !A){
8220                 this.dom.style.width = this.addUnits(width);
8221                 this.dom.style.height = this.addUnits(height);
8222             }else{
8223                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8224             }
8225             return this;
8226         },
8227
8228         /**
8229          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8230          * @param {Number} x X value for new position (coordinates are page-based)
8231          * @param {Number} y Y value for new position (coordinates are page-based)
8232          * @param {Number} width The new width
8233          * @param {Number} height The new height
8234          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8235          * @return {Roo.Element} this
8236          */
8237         setBounds : function(x, y, width, height, animate){
8238             if(!animate || !A){
8239                 this.setSize(width, height);
8240                 this.setLocation(x, y);
8241             }else{
8242                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8243                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8244                               this.preanim(arguments, 4), 'motion');
8245             }
8246             return this;
8247         },
8248
8249         /**
8250          * 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.
8251          * @param {Roo.lib.Region} region The region to fill
8252          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8253          * @return {Roo.Element} this
8254          */
8255         setRegion : function(region, animate){
8256             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8257             return this;
8258         },
8259
8260         /**
8261          * Appends an event handler
8262          *
8263          * @param {String}   eventName     The type of event to append
8264          * @param {Function} fn        The method the event invokes
8265          * @param {Object} scope       (optional) The scope (this object) of the fn
8266          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8267          */
8268         addListener : function(eventName, fn, scope, options)
8269         {
8270             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8271                 this.addListener('touchstart', this.onTapHandler, this);
8272             }
8273             
8274             // we need to handle a special case where dom element is a svg element.
8275             // in this case we do not actua
8276             if (!this.dom) {
8277                 return;
8278             }
8279             
8280             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8281                 if (typeof(this.listeners[eventName]) == 'undefined') {
8282                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8283                 }
8284                 this.listeners[eventName].addListener(fn, scope, options);
8285                 return;
8286             }
8287             
8288                 
8289             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8290             
8291             
8292         },
8293         tapedTwice : false,
8294         onTapHandler : function(event)
8295         {
8296             if(!this.tapedTwice) {
8297                 this.tapedTwice = true;
8298                 var s = this;
8299                 setTimeout( function() {
8300                     s.tapedTwice = false;
8301                 }, 300 );
8302                 return;
8303             }
8304             event.preventDefault();
8305             var revent = new MouseEvent('dblclick',  {
8306                 view: window,
8307                 bubbles: true,
8308                 cancelable: true
8309             });
8310              
8311             this.dom.dispatchEvent(revent);
8312             //action on double tap goes below
8313              
8314         }, 
8315  
8316         /**
8317          * Removes an event handler from this element
8318          * @param {String} eventName the type of event to remove
8319          * @param {Function} fn the method the event invokes
8320          * @param {Function} scope (needed for svg fake listeners)
8321          * @return {Roo.Element} this
8322          */
8323         removeListener : function(eventName, fn, scope){
8324             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8325             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8326                 return this;
8327             }
8328             this.listeners[eventName].removeListener(fn, scope);
8329             return this;
8330         },
8331
8332         /**
8333          * Removes all previous added listeners from this element
8334          * @return {Roo.Element} this
8335          */
8336         removeAllListeners : function(){
8337             E.purgeElement(this.dom);
8338             this.listeners = {};
8339             return this;
8340         },
8341
8342         relayEvent : function(eventName, observable){
8343             this.on(eventName, function(e){
8344                 observable.fireEvent(eventName, e);
8345             });
8346         },
8347
8348         
8349         /**
8350          * Set the opacity of the element
8351          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8352          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8353          * @return {Roo.Element} this
8354          */
8355          setOpacity : function(opacity, animate){
8356             if(!animate || !A){
8357                 var s = this.dom.style;
8358                 if(Roo.isIE){
8359                     s.zoom = 1;
8360                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8361                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8362                 }else{
8363                     s.opacity = opacity;
8364                 }
8365             }else{
8366                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8367             }
8368             return this;
8369         },
8370
8371         /**
8372          * Gets the left X coordinate
8373          * @param {Boolean} local True to get the local css position instead of page coordinate
8374          * @return {Number}
8375          */
8376         getLeft : function(local){
8377             if(!local){
8378                 return this.getX();
8379             }else{
8380                 return parseInt(this.getStyle("left"), 10) || 0;
8381             }
8382         },
8383
8384         /**
8385          * Gets the right X coordinate of the element (element X position + element width)
8386          * @param {Boolean} local True to get the local css position instead of page coordinate
8387          * @return {Number}
8388          */
8389         getRight : function(local){
8390             if(!local){
8391                 return this.getX() + this.getWidth();
8392             }else{
8393                 return (this.getLeft(true) + this.getWidth()) || 0;
8394             }
8395         },
8396
8397         /**
8398          * Gets the top Y coordinate
8399          * @param {Boolean} local True to get the local css position instead of page coordinate
8400          * @return {Number}
8401          */
8402         getTop : function(local) {
8403             if(!local){
8404                 return this.getY();
8405             }else{
8406                 return parseInt(this.getStyle("top"), 10) || 0;
8407             }
8408         },
8409
8410         /**
8411          * Gets the bottom Y coordinate of the element (element Y position + element height)
8412          * @param {Boolean} local True to get the local css position instead of page coordinate
8413          * @return {Number}
8414          */
8415         getBottom : function(local){
8416             if(!local){
8417                 return this.getY() + this.getHeight();
8418             }else{
8419                 return (this.getTop(true) + this.getHeight()) || 0;
8420             }
8421         },
8422
8423         /**
8424         * Initializes positioning on this element. If a desired position is not passed, it will make the
8425         * the element positioned relative IF it is not already positioned.
8426         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8427         * @param {Number} zIndex (optional) The zIndex to apply
8428         * @param {Number} x (optional) Set the page X position
8429         * @param {Number} y (optional) Set the page Y position
8430         */
8431         position : function(pos, zIndex, x, y){
8432             if(!pos){
8433                if(this.getStyle('position') == 'static'){
8434                    this.setStyle('position', 'relative');
8435                }
8436             }else{
8437                 this.setStyle("position", pos);
8438             }
8439             if(zIndex){
8440                 this.setStyle("z-index", zIndex);
8441             }
8442             if(x !== undefined && y !== undefined){
8443                 this.setXY([x, y]);
8444             }else if(x !== undefined){
8445                 this.setX(x);
8446             }else if(y !== undefined){
8447                 this.setY(y);
8448             }
8449         },
8450
8451         /**
8452         * Clear positioning back to the default when the document was loaded
8453         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8454         * @return {Roo.Element} this
8455          */
8456         clearPositioning : function(value){
8457             value = value ||'';
8458             this.setStyle({
8459                 "left": value,
8460                 "right": value,
8461                 "top": value,
8462                 "bottom": value,
8463                 "z-index": "",
8464                 "position" : "static"
8465             });
8466             return this;
8467         },
8468
8469         /**
8470         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8471         * snapshot before performing an update and then restoring the element.
8472         * @return {Object}
8473         */
8474         getPositioning : function(){
8475             var l = this.getStyle("left");
8476             var t = this.getStyle("top");
8477             return {
8478                 "position" : this.getStyle("position"),
8479                 "left" : l,
8480                 "right" : l ? "" : this.getStyle("right"),
8481                 "top" : t,
8482                 "bottom" : t ? "" : this.getStyle("bottom"),
8483                 "z-index" : this.getStyle("z-index")
8484             };
8485         },
8486
8487         /**
8488          * Gets the width of the border(s) for the specified side(s)
8489          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8490          * passing lr would get the border (l)eft width + the border (r)ight width.
8491          * @return {Number} The width of the sides passed added together
8492          */
8493         getBorderWidth : function(side){
8494             return this.addStyles(side, El.borders);
8495         },
8496
8497         /**
8498          * Gets the width of the padding(s) for the specified side(s)
8499          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8500          * passing lr would get the padding (l)eft + the padding (r)ight.
8501          * @return {Number} The padding of the sides passed added together
8502          */
8503         getPadding : function(side){
8504             return this.addStyles(side, El.paddings);
8505         },
8506
8507         /**
8508         * Set positioning with an object returned by getPositioning().
8509         * @param {Object} posCfg
8510         * @return {Roo.Element} this
8511          */
8512         setPositioning : function(pc){
8513             this.applyStyles(pc);
8514             if(pc.right == "auto"){
8515                 this.dom.style.right = "";
8516             }
8517             if(pc.bottom == "auto"){
8518                 this.dom.style.bottom = "";
8519             }
8520             return this;
8521         },
8522
8523         // private
8524         fixDisplay : function(){
8525             if(this.getStyle("display") == "none"){
8526                 this.setStyle("visibility", "hidden");
8527                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8528                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8529                     this.setStyle("display", "block");
8530                 }
8531             }
8532         },
8533
8534         /**
8535          * Quick set left and top adding default units
8536          * @param {String} left The left CSS property value
8537          * @param {String} top The top CSS property value
8538          * @return {Roo.Element} this
8539          */
8540          setLeftTop : function(left, top){
8541             this.dom.style.left = this.addUnits(left);
8542             this.dom.style.top = this.addUnits(top);
8543             return this;
8544         },
8545
8546         /**
8547          * Move this element relative to its current position.
8548          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8549          * @param {Number} distance How far to move the element in pixels
8550          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8551          * @return {Roo.Element} this
8552          */
8553          move : function(direction, distance, animate){
8554             var xy = this.getXY();
8555             direction = direction.toLowerCase();
8556             switch(direction){
8557                 case "l":
8558                 case "left":
8559                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8560                     break;
8561                case "r":
8562                case "right":
8563                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8564                     break;
8565                case "t":
8566                case "top":
8567                case "up":
8568                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8569                     break;
8570                case "b":
8571                case "bottom":
8572                case "down":
8573                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8574                     break;
8575             }
8576             return this;
8577         },
8578
8579         /**
8580          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8581          * @return {Roo.Element} this
8582          */
8583         clip : function(){
8584             if(!this.isClipped){
8585                this.isClipped = true;
8586                this.originalClip = {
8587                    "o": this.getStyle("overflow"),
8588                    "x": this.getStyle("overflow-x"),
8589                    "y": this.getStyle("overflow-y")
8590                };
8591                this.setStyle("overflow", "hidden");
8592                this.setStyle("overflow-x", "hidden");
8593                this.setStyle("overflow-y", "hidden");
8594             }
8595             return this;
8596         },
8597
8598         /**
8599          *  Return clipping (overflow) to original clipping before clip() was called
8600          * @return {Roo.Element} this
8601          */
8602         unclip : function(){
8603             if(this.isClipped){
8604                 this.isClipped = false;
8605                 var o = this.originalClip;
8606                 if(o.o){this.setStyle("overflow", o.o);}
8607                 if(o.x){this.setStyle("overflow-x", o.x);}
8608                 if(o.y){this.setStyle("overflow-y", o.y);}
8609             }
8610             return this;
8611         },
8612
8613
8614         /**
8615          * Gets the x,y coordinates specified by the anchor position on the element.
8616          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8617          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8618          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8619          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8620          * @return {Array} [x, y] An array containing the element's x and y coordinates
8621          */
8622         getAnchorXY : function(anchor, local, s){
8623             //Passing a different size is useful for pre-calculating anchors,
8624             //especially for anchored animations that change the el size.
8625
8626             var w, h, vp = false;
8627             if(!s){
8628                 var d = this.dom;
8629                 if(d == document.body || d == document){
8630                     vp = true;
8631                     w = D.getViewWidth(); h = D.getViewHeight();
8632                 }else{
8633                     w = this.getWidth(); h = this.getHeight();
8634                 }
8635             }else{
8636                 w = s.width;  h = s.height;
8637             }
8638             var x = 0, y = 0, r = Math.round;
8639             switch((anchor || "tl").toLowerCase()){
8640                 case "c":
8641                     x = r(w*.5);
8642                     y = r(h*.5);
8643                 break;
8644                 case "t":
8645                     x = r(w*.5);
8646                     y = 0;
8647                 break;
8648                 case "l":
8649                     x = 0;
8650                     y = r(h*.5);
8651                 break;
8652                 case "r":
8653                     x = w;
8654                     y = r(h*.5);
8655                 break;
8656                 case "b":
8657                     x = r(w*.5);
8658                     y = h;
8659                 break;
8660                 case "tl":
8661                     x = 0;
8662                     y = 0;
8663                 break;
8664                 case "bl":
8665                     x = 0;
8666                     y = h;
8667                 break;
8668                 case "br":
8669                     x = w;
8670                     y = h;
8671                 break;
8672                 case "tr":
8673                     x = w;
8674                     y = 0;
8675                 break;
8676             }
8677             if(local === true){
8678                 return [x, y];
8679             }
8680             if(vp){
8681                 var sc = this.getScroll();
8682                 return [x + sc.left, y + sc.top];
8683             }
8684             //Add the element's offset xy
8685             var o = this.getXY();
8686             return [x+o[0], y+o[1]];
8687         },
8688
8689         /**
8690          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8691          * supported position values.
8692          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8693          * @param {String} position The position to align to.
8694          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8695          * @return {Array} [x, y]
8696          */
8697         getAlignToXY : function(el, p, o)
8698         {
8699             el = Roo.get(el);
8700             var d = this.dom;
8701             if(!el.dom){
8702                 throw "Element.alignTo with an element that doesn't exist";
8703             }
8704             var c = false; //constrain to viewport
8705             var p1 = "", p2 = "";
8706             o = o || [0,0];
8707
8708             if(!p){
8709                 p = "tl-bl";
8710             }else if(p == "?"){
8711                 p = "tl-bl?";
8712             }else if(p.indexOf("-") == -1){
8713                 p = "tl-" + p;
8714             }
8715             p = p.toLowerCase();
8716             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8717             if(!m){
8718                throw "Element.alignTo with an invalid alignment " + p;
8719             }
8720             p1 = m[1]; p2 = m[2]; c = !!m[3];
8721
8722             //Subtract the aligned el's internal xy from the target's offset xy
8723             //plus custom offset to get the aligned el's new offset xy
8724             var a1 = this.getAnchorXY(p1, true);
8725             var a2 = el.getAnchorXY(p2, false);
8726             var x = a2[0] - a1[0] + o[0];
8727             var y = a2[1] - a1[1] + o[1];
8728             if(c){
8729                 //constrain the aligned el to viewport if necessary
8730                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8731                 // 5px of margin for ie
8732                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8733
8734                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8735                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8736                 //otherwise swap the aligned el to the opposite border of the target.
8737                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8738                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8739                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8740                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8741
8742                var doc = document;
8743                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8744                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8745
8746                if((x+w) > dw + scrollX){
8747                     x = swapX ? r.left-w : dw+scrollX-w;
8748                 }
8749                if(x < scrollX){
8750                    x = swapX ? r.right : scrollX;
8751                }
8752                if((y+h) > dh + scrollY){
8753                     y = swapY ? r.top-h : dh+scrollY-h;
8754                 }
8755                if (y < scrollY){
8756                    y = swapY ? r.bottom : scrollY;
8757                }
8758             }
8759             return [x,y];
8760         },
8761
8762         // private
8763         getConstrainToXY : function(){
8764             var os = {top:0, left:0, bottom:0, right: 0};
8765
8766             return function(el, local, offsets, proposedXY){
8767                 el = Roo.get(el);
8768                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8769
8770                 var vw, vh, vx = 0, vy = 0;
8771                 if(el.dom == document.body || el.dom == document){
8772                     vw = Roo.lib.Dom.getViewWidth();
8773                     vh = Roo.lib.Dom.getViewHeight();
8774                 }else{
8775                     vw = el.dom.clientWidth;
8776                     vh = el.dom.clientHeight;
8777                     if(!local){
8778                         var vxy = el.getXY();
8779                         vx = vxy[0];
8780                         vy = vxy[1];
8781                     }
8782                 }
8783
8784                 var s = el.getScroll();
8785
8786                 vx += offsets.left + s.left;
8787                 vy += offsets.top + s.top;
8788
8789                 vw -= offsets.right;
8790                 vh -= offsets.bottom;
8791
8792                 var vr = vx+vw;
8793                 var vb = vy+vh;
8794
8795                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8796                 var x = xy[0], y = xy[1];
8797                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8798
8799                 // only move it if it needs it
8800                 var moved = false;
8801
8802                 // first validate right/bottom
8803                 if((x + w) > vr){
8804                     x = vr - w;
8805                     moved = true;
8806                 }
8807                 if((y + h) > vb){
8808                     y = vb - h;
8809                     moved = true;
8810                 }
8811                 // then make sure top/left isn't negative
8812                 if(x < vx){
8813                     x = vx;
8814                     moved = true;
8815                 }
8816                 if(y < vy){
8817                     y = vy;
8818                     moved = true;
8819                 }
8820                 return moved ? [x, y] : false;
8821             };
8822         }(),
8823
8824         // private
8825         adjustForConstraints : function(xy, parent, offsets){
8826             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8827         },
8828
8829         /**
8830          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8831          * document it aligns it to the viewport.
8832          * The position parameter is optional, and can be specified in any one of the following formats:
8833          * <ul>
8834          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8835          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8836          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8837          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8838          *   <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
8839          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8840          * </ul>
8841          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8842          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8843          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8844          * that specified in order to enforce the viewport constraints.
8845          * Following are all of the supported anchor positions:
8846     <pre>
8847     Value  Description
8848     -----  -----------------------------
8849     tl     The top left corner (default)
8850     t      The center of the top edge
8851     tr     The top right corner
8852     l      The center of the left edge
8853     c      In the center of the element
8854     r      The center of the right edge
8855     bl     The bottom left corner
8856     b      The center of the bottom edge
8857     br     The bottom right corner
8858     </pre>
8859     Example Usage:
8860     <pre><code>
8861     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8862     el.alignTo("other-el");
8863
8864     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8865     el.alignTo("other-el", "tr?");
8866
8867     // align the bottom right corner of el with the center left edge of other-el
8868     el.alignTo("other-el", "br-l?");
8869
8870     // align the center of el with the bottom left corner of other-el and
8871     // adjust the x position by -6 pixels (and the y position by 0)
8872     el.alignTo("other-el", "c-bl", [-6, 0]);
8873     </code></pre>
8874          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8875          * @param {String} position The position to align to.
8876          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8877          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8878          * @return {Roo.Element} this
8879          */
8880         alignTo : function(element, position, offsets, animate){
8881             var xy = this.getAlignToXY(element, position, offsets);
8882             this.setXY(xy, this.preanim(arguments, 3));
8883             return this;
8884         },
8885
8886         /**
8887          * Anchors an element to another element and realigns it when the window is resized.
8888          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8889          * @param {String} position The position to align to.
8890          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8891          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8892          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8893          * is a number, it is used as the buffer delay (defaults to 50ms).
8894          * @param {Function} callback The function to call after the animation finishes
8895          * @return {Roo.Element} this
8896          */
8897         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8898             var action = function(){
8899                 this.alignTo(el, alignment, offsets, animate);
8900                 Roo.callback(callback, this);
8901             };
8902             Roo.EventManager.onWindowResize(action, this);
8903             var tm = typeof monitorScroll;
8904             if(tm != 'undefined'){
8905                 Roo.EventManager.on(window, 'scroll', action, this,
8906                     {buffer: tm == 'number' ? monitorScroll : 50});
8907             }
8908             action.call(this); // align immediately
8909             return this;
8910         },
8911         /**
8912          * Clears any opacity settings from this element. Required in some cases for IE.
8913          * @return {Roo.Element} this
8914          */
8915         clearOpacity : function(){
8916             if (window.ActiveXObject) {
8917                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8918                     this.dom.style.filter = "";
8919                 }
8920             } else {
8921                 this.dom.style.opacity = "";
8922                 this.dom.style["-moz-opacity"] = "";
8923                 this.dom.style["-khtml-opacity"] = "";
8924             }
8925             return this;
8926         },
8927
8928         /**
8929          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8930          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8931          * @return {Roo.Element} this
8932          */
8933         hide : function(animate){
8934             this.setVisible(false, this.preanim(arguments, 0));
8935             return this;
8936         },
8937
8938         /**
8939         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8940         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8941          * @return {Roo.Element} this
8942          */
8943         show : function(animate){
8944             this.setVisible(true, this.preanim(arguments, 0));
8945             return this;
8946         },
8947
8948         /**
8949          * @private Test if size has a unit, otherwise appends the default
8950          */
8951         addUnits : function(size){
8952             return Roo.Element.addUnits(size, this.defaultUnit);
8953         },
8954
8955         /**
8956          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8957          * @return {Roo.Element} this
8958          */
8959         beginMeasure : function(){
8960             var el = this.dom;
8961             if(el.offsetWidth || el.offsetHeight){
8962                 return this; // offsets work already
8963             }
8964             var changed = [];
8965             var p = this.dom, b = document.body; // start with this element
8966             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8967                 var pe = Roo.get(p);
8968                 if(pe.getStyle('display') == 'none'){
8969                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8970                     p.style.visibility = "hidden";
8971                     p.style.display = "block";
8972                 }
8973                 p = p.parentNode;
8974             }
8975             this._measureChanged = changed;
8976             return this;
8977
8978         },
8979
8980         /**
8981          * Restores displays to before beginMeasure was called
8982          * @return {Roo.Element} this
8983          */
8984         endMeasure : function(){
8985             var changed = this._measureChanged;
8986             if(changed){
8987                 for(var i = 0, len = changed.length; i < len; i++) {
8988                     var r = changed[i];
8989                     r.el.style.visibility = r.visibility;
8990                     r.el.style.display = "none";
8991                 }
8992                 this._measureChanged = null;
8993             }
8994             return this;
8995         },
8996
8997         /**
8998         * Update the innerHTML of this element, optionally searching for and processing scripts
8999         * @param {String} html The new HTML
9000         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9001         * @param {Function} callback For async script loading you can be noticed when the update completes
9002         * @return {Roo.Element} this
9003          */
9004         update : function(html, loadScripts, callback){
9005             if(typeof html == "undefined"){
9006                 html = "";
9007             }
9008             if(loadScripts !== true){
9009                 this.dom.innerHTML = html;
9010                 if(typeof callback == "function"){
9011                     callback();
9012                 }
9013                 return this;
9014             }
9015             var id = Roo.id();
9016             var dom = this.dom;
9017
9018             html += '<span id="' + id + '"></span>';
9019
9020             E.onAvailable(id, function(){
9021                 var hd = document.getElementsByTagName("head")[0];
9022                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9023                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9024                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9025
9026                 var match;
9027                 while(match = re.exec(html)){
9028                     var attrs = match[1];
9029                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9030                     if(srcMatch && srcMatch[2]){
9031                        var s = document.createElement("script");
9032                        s.src = srcMatch[2];
9033                        var typeMatch = attrs.match(typeRe);
9034                        if(typeMatch && typeMatch[2]){
9035                            s.type = typeMatch[2];
9036                        }
9037                        hd.appendChild(s);
9038                     }else if(match[2] && match[2].length > 0){
9039                         if(window.execScript) {
9040                            window.execScript(match[2]);
9041                         } else {
9042                             /**
9043                              * eval:var:id
9044                              * eval:var:dom
9045                              * eval:var:html
9046                              * 
9047                              */
9048                            window.eval(match[2]);
9049                         }
9050                     }
9051                 }
9052                 var el = document.getElementById(id);
9053                 if(el){el.parentNode.removeChild(el);}
9054                 if(typeof callback == "function"){
9055                     callback();
9056                 }
9057             });
9058             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9059             return this;
9060         },
9061
9062         /**
9063          * Direct access to the UpdateManager update() method (takes the same parameters).
9064          * @param {String/Function} url The url for this request or a function to call to get the url
9065          * @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}
9066          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9067          * @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.
9068          * @return {Roo.Element} this
9069          */
9070         load : function(){
9071             var um = this.getUpdateManager();
9072             um.update.apply(um, arguments);
9073             return this;
9074         },
9075
9076         /**
9077         * Gets this element's UpdateManager
9078         * @return {Roo.UpdateManager} The UpdateManager
9079         */
9080         getUpdateManager : function(){
9081             if(!this.updateManager){
9082                 this.updateManager = new Roo.UpdateManager(this);
9083             }
9084             return this.updateManager;
9085         },
9086
9087         /**
9088          * Disables text selection for this element (normalized across browsers)
9089          * @return {Roo.Element} this
9090          */
9091         unselectable : function(){
9092             this.dom.unselectable = "on";
9093             this.swallowEvent("selectstart", true);
9094             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9095             this.addClass("x-unselectable");
9096             return this;
9097         },
9098
9099         /**
9100         * Calculates the x, y to center this element on the screen
9101         * @return {Array} The x, y values [x, y]
9102         */
9103         getCenterXY : function(){
9104             return this.getAlignToXY(document, 'c-c');
9105         },
9106
9107         /**
9108         * Centers the Element in either the viewport, or another Element.
9109         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9110         */
9111         center : function(centerIn){
9112             this.alignTo(centerIn || document, 'c-c');
9113             return this;
9114         },
9115
9116         /**
9117          * Tests various css rules/browsers to determine if this element uses a border box
9118          * @return {Boolean}
9119          */
9120         isBorderBox : function(){
9121             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9122         },
9123
9124         /**
9125          * Return a box {x, y, width, height} that can be used to set another elements
9126          * size/location to match this element.
9127          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9128          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9129          * @return {Object} box An object in the format {x, y, width, height}
9130          */
9131         getBox : function(contentBox, local){
9132             var xy;
9133             if(!local){
9134                 xy = this.getXY();
9135             }else{
9136                 var left = parseInt(this.getStyle("left"), 10) || 0;
9137                 var top = parseInt(this.getStyle("top"), 10) || 0;
9138                 xy = [left, top];
9139             }
9140             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9141             if(!contentBox){
9142                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9143             }else{
9144                 var l = this.getBorderWidth("l")+this.getPadding("l");
9145                 var r = this.getBorderWidth("r")+this.getPadding("r");
9146                 var t = this.getBorderWidth("t")+this.getPadding("t");
9147                 var b = this.getBorderWidth("b")+this.getPadding("b");
9148                 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)};
9149             }
9150             bx.right = bx.x + bx.width;
9151             bx.bottom = bx.y + bx.height;
9152             return bx;
9153         },
9154
9155         /**
9156          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9157          for more information about the sides.
9158          * @param {String} sides
9159          * @return {Number}
9160          */
9161         getFrameWidth : function(sides, onlyContentBox){
9162             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9163         },
9164
9165         /**
9166          * 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.
9167          * @param {Object} box The box to fill {x, y, width, height}
9168          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9170          * @return {Roo.Element} this
9171          */
9172         setBox : function(box, adjust, animate){
9173             var w = box.width, h = box.height;
9174             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9175                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9176                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9177             }
9178             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9179             return this;
9180         },
9181
9182         /**
9183          * Forces the browser to repaint this element
9184          * @return {Roo.Element} this
9185          */
9186          repaint : function(){
9187             var dom = this.dom;
9188             this.addClass("x-repaint");
9189             setTimeout(function(){
9190                 Roo.get(dom).removeClass("x-repaint");
9191             }, 1);
9192             return this;
9193         },
9194
9195         /**
9196          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9197          * then it returns the calculated width of the sides (see getPadding)
9198          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9199          * @return {Object/Number}
9200          */
9201         getMargins : function(side){
9202             if(!side){
9203                 return {
9204                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9205                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9206                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9207                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9208                 };
9209             }else{
9210                 return this.addStyles(side, El.margins);
9211              }
9212         },
9213
9214         // private
9215         addStyles : function(sides, styles){
9216             var val = 0, v, w;
9217             for(var i = 0, len = sides.length; i < len; i++){
9218                 v = this.getStyle(styles[sides.charAt(i)]);
9219                 if(v){
9220                      w = parseInt(v, 10);
9221                      if(w){ val += w; }
9222                 }
9223             }
9224             return val;
9225         },
9226
9227         /**
9228          * Creates a proxy element of this element
9229          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9230          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9231          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9232          * @return {Roo.Element} The new proxy element
9233          */
9234         createProxy : function(config, renderTo, matchBox){
9235             if(renderTo){
9236                 renderTo = Roo.getDom(renderTo);
9237             }else{
9238                 renderTo = document.body;
9239             }
9240             config = typeof config == "object" ?
9241                 config : {tag : "div", cls: config};
9242             var proxy = Roo.DomHelper.append(renderTo, config, true);
9243             if(matchBox){
9244                proxy.setBox(this.getBox());
9245             }
9246             return proxy;
9247         },
9248
9249         /**
9250          * Puts a mask over this element to disable user interaction. Requires core.css.
9251          * This method can only be applied to elements which accept child nodes.
9252          * @param {String} msg (optional) A message to display in the mask
9253          * @param {String} msgCls (optional) A css class to apply to the msg element
9254          * @return {Element} The mask  element
9255          */
9256         mask : function(msg, msgCls)
9257         {
9258             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9259                 this.setStyle("position", "relative");
9260             }
9261             if(!this._mask){
9262                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9263             }
9264             
9265             this.addClass("x-masked");
9266             this._mask.setDisplayed(true);
9267             
9268             // we wander
9269             var z = 0;
9270             var dom = this.dom;
9271             while (dom && dom.style) {
9272                 if (!isNaN(parseInt(dom.style.zIndex))) {
9273                     z = Math.max(z, parseInt(dom.style.zIndex));
9274                 }
9275                 dom = dom.parentNode;
9276             }
9277             // if we are masking the body - then it hides everything..
9278             if (this.dom == document.body) {
9279                 z = 1000000;
9280                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9281                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9282             }
9283            
9284             if(typeof msg == 'string'){
9285                 if(!this._maskMsg){
9286                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9287                         cls: "roo-el-mask-msg", 
9288                         cn: [
9289                             {
9290                                 tag: 'i',
9291                                 cls: 'fa fa-spinner fa-spin'
9292                             },
9293                             {
9294                                 tag: 'div'
9295                             }   
9296                         ]
9297                     }, true);
9298                 }
9299                 var mm = this._maskMsg;
9300                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9301                 if (mm.dom.lastChild) { // weird IE issue?
9302                     mm.dom.lastChild.innerHTML = msg;
9303                 }
9304                 mm.setDisplayed(true);
9305                 mm.center(this);
9306                 mm.setStyle('z-index', z + 102);
9307             }
9308             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9309                 this._mask.setHeight(this.getHeight());
9310             }
9311             this._mask.setStyle('z-index', z + 100);
9312             
9313             return this._mask;
9314         },
9315
9316         /**
9317          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9318          * it is cached for reuse.
9319          */
9320         unmask : function(removeEl){
9321             if(this._mask){
9322                 if(removeEl === true){
9323                     this._mask.remove();
9324                     delete this._mask;
9325                     if(this._maskMsg){
9326                         this._maskMsg.remove();
9327                         delete this._maskMsg;
9328                     }
9329                 }else{
9330                     this._mask.setDisplayed(false);
9331                     if(this._maskMsg){
9332                         this._maskMsg.setDisplayed(false);
9333                     }
9334                 }
9335             }
9336             this.removeClass("x-masked");
9337         },
9338
9339         /**
9340          * Returns true if this element is masked
9341          * @return {Boolean}
9342          */
9343         isMasked : function(){
9344             return this._mask && this._mask.isVisible();
9345         },
9346
9347         /**
9348          * Creates an iframe shim for this element to keep selects and other windowed objects from
9349          * showing through.
9350          * @return {Roo.Element} The new shim element
9351          */
9352         createShim : function(){
9353             var el = document.createElement('iframe');
9354             el.frameBorder = 'no';
9355             el.className = 'roo-shim';
9356             if(Roo.isIE && Roo.isSecure){
9357                 el.src = Roo.SSL_SECURE_URL;
9358             }
9359             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9360             shim.autoBoxAdjust = false;
9361             return shim;
9362         },
9363
9364         /**
9365          * Removes this element from the DOM and deletes it from the cache
9366          */
9367         remove : function(){
9368             if(this.dom.parentNode){
9369                 this.dom.parentNode.removeChild(this.dom);
9370             }
9371             delete El.cache[this.dom.id];
9372         },
9373
9374         /**
9375          * Sets up event handlers to add and remove a css class when the mouse is over this element
9376          * @param {String} className
9377          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9378          * mouseout events for children elements
9379          * @return {Roo.Element} this
9380          */
9381         addClassOnOver : function(className, preventFlicker){
9382             this.on("mouseover", function(){
9383                 Roo.fly(this, '_internal').addClass(className);
9384             }, this.dom);
9385             var removeFn = function(e){
9386                 if(preventFlicker !== true || !e.within(this, true)){
9387                     Roo.fly(this, '_internal').removeClass(className);
9388                 }
9389             };
9390             this.on("mouseout", removeFn, this.dom);
9391             return this;
9392         },
9393
9394         /**
9395          * Sets up event handlers to add and remove a css class when this element has the focus
9396          * @param {String} className
9397          * @return {Roo.Element} this
9398          */
9399         addClassOnFocus : function(className){
9400             this.on("focus", function(){
9401                 Roo.fly(this, '_internal').addClass(className);
9402             }, this.dom);
9403             this.on("blur", function(){
9404                 Roo.fly(this, '_internal').removeClass(className);
9405             }, this.dom);
9406             return this;
9407         },
9408         /**
9409          * 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)
9410          * @param {String} className
9411          * @return {Roo.Element} this
9412          */
9413         addClassOnClick : function(className){
9414             var dom = this.dom;
9415             this.on("mousedown", function(){
9416                 Roo.fly(dom, '_internal').addClass(className);
9417                 var d = Roo.get(document);
9418                 var fn = function(){
9419                     Roo.fly(dom, '_internal').removeClass(className);
9420                     d.removeListener("mouseup", fn);
9421                 };
9422                 d.on("mouseup", fn);
9423             });
9424             return this;
9425         },
9426
9427         /**
9428          * Stops the specified event from bubbling and optionally prevents the default action
9429          * @param {String} eventName
9430          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9431          * @return {Roo.Element} this
9432          */
9433         swallowEvent : function(eventName, preventDefault){
9434             var fn = function(e){
9435                 e.stopPropagation();
9436                 if(preventDefault){
9437                     e.preventDefault();
9438                 }
9439             };
9440             if(eventName instanceof Array){
9441                 for(var i = 0, len = eventName.length; i < len; i++){
9442                      this.on(eventName[i], fn);
9443                 }
9444                 return this;
9445             }
9446             this.on(eventName, fn);
9447             return this;
9448         },
9449
9450         /**
9451          * @private
9452          */
9453         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9454
9455         /**
9456          * Sizes this element to its parent element's dimensions performing
9457          * neccessary box adjustments.
9458          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9459          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9460          * @return {Roo.Element} this
9461          */
9462         fitToParent : function(monitorResize, targetParent) {
9463           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9464           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9465           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9466             return this;
9467           }
9468           var p = Roo.get(targetParent || this.dom.parentNode);
9469           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9470           if (monitorResize === true) {
9471             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9472             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9473           }
9474           return this;
9475         },
9476
9477         /**
9478          * Gets the next sibling, skipping text nodes
9479          * @return {HTMLElement} The next sibling or null
9480          */
9481         getNextSibling : function(){
9482             var n = this.dom.nextSibling;
9483             while(n && n.nodeType != 1){
9484                 n = n.nextSibling;
9485             }
9486             return n;
9487         },
9488
9489         /**
9490          * Gets the previous sibling, skipping text nodes
9491          * @return {HTMLElement} The previous sibling or null
9492          */
9493         getPrevSibling : function(){
9494             var n = this.dom.previousSibling;
9495             while(n && n.nodeType != 1){
9496                 n = n.previousSibling;
9497             }
9498             return n;
9499         },
9500
9501
9502         /**
9503          * Appends the passed element(s) to this element
9504          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9505          * @return {Roo.Element} this
9506          */
9507         appendChild: function(el){
9508             el = Roo.get(el);
9509             el.appendTo(this);
9510             return this;
9511         },
9512
9513         /**
9514          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9515          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9516          * automatically generated with the specified attributes.
9517          * @param {HTMLElement} insertBefore (optional) a child element of this element
9518          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9519          * @return {Roo.Element} The new child element
9520          */
9521         createChild: function(config, insertBefore, returnDom){
9522             config = config || {tag:'div'};
9523             if(insertBefore){
9524                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9525             }
9526             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9527         },
9528
9529         /**
9530          * Appends this element to the passed element
9531          * @param {String/HTMLElement/Element} el The new parent element
9532          * @return {Roo.Element} this
9533          */
9534         appendTo: function(el){
9535             el = Roo.getDom(el);
9536             el.appendChild(this.dom);
9537             return this;
9538         },
9539
9540         /**
9541          * Inserts this element before the passed element in the DOM
9542          * @param {String/HTMLElement/Element} el The element to insert before
9543          * @return {Roo.Element} this
9544          */
9545         insertBefore: function(el){
9546             el = Roo.getDom(el);
9547             el.parentNode.insertBefore(this.dom, el);
9548             return this;
9549         },
9550
9551         /**
9552          * Inserts this element after the passed element in the DOM
9553          * @param {String/HTMLElement/Element} el The element to insert after
9554          * @return {Roo.Element} this
9555          */
9556         insertAfter: function(el){
9557             el = Roo.getDom(el);
9558             el.parentNode.insertBefore(this.dom, el.nextSibling);
9559             return this;
9560         },
9561
9562         /**
9563          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9564          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9565          * @return {Roo.Element} The new child
9566          */
9567         insertFirst: function(el, returnDom){
9568             el = el || {};
9569             if(typeof el == 'object' && !el.nodeType){ // dh config
9570                 return this.createChild(el, this.dom.firstChild, returnDom);
9571             }else{
9572                 el = Roo.getDom(el);
9573                 this.dom.insertBefore(el, this.dom.firstChild);
9574                 return !returnDom ? Roo.get(el) : el;
9575             }
9576         },
9577
9578         /**
9579          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9580          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9581          * @param {String} where (optional) 'before' or 'after' defaults to before
9582          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9583          * @return {Roo.Element} the inserted Element
9584          */
9585         insertSibling: function(el, where, returnDom){
9586             where = where ? where.toLowerCase() : 'before';
9587             el = el || {};
9588             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9589
9590             if(typeof el == 'object' && !el.nodeType){ // dh config
9591                 if(where == 'after' && !this.dom.nextSibling){
9592                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9593                 }else{
9594                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9595                 }
9596
9597             }else{
9598                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9599                             where == 'before' ? this.dom : this.dom.nextSibling);
9600                 if(!returnDom){
9601                     rt = Roo.get(rt);
9602                 }
9603             }
9604             return rt;
9605         },
9606
9607         /**
9608          * Creates and wraps this element with another element
9609          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9610          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9611          * @return {HTMLElement/Element} The newly created wrapper element
9612          */
9613         wrap: function(config, returnDom){
9614             if(!config){
9615                 config = {tag: "div"};
9616             }
9617             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9618             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9619             return newEl;
9620         },
9621
9622         /**
9623          * Replaces the passed element with this element
9624          * @param {String/HTMLElement/Element} el The element to replace
9625          * @return {Roo.Element} this
9626          */
9627         replace: function(el){
9628             el = Roo.get(el);
9629             this.insertBefore(el);
9630             el.remove();
9631             return this;
9632         },
9633
9634         /**
9635          * Inserts an html fragment into this element
9636          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9637          * @param {String} html The HTML fragment
9638          * @param {Boolean} returnEl True to return an Roo.Element
9639          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9640          */
9641         insertHtml : function(where, html, returnEl){
9642             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9643             return returnEl ? Roo.get(el) : el;
9644         },
9645
9646         /**
9647          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9648          * @param {Object} o The object with the attributes
9649          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9650          * @return {Roo.Element} this
9651          */
9652         set : function(o, useSet){
9653             var el = this.dom;
9654             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9655             for(var attr in o){
9656                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9657                 if(attr=="cls"){
9658                     el.className = o["cls"];
9659                 }else{
9660                     if(useSet) {
9661                         el.setAttribute(attr, o[attr]);
9662                     } else {
9663                         el[attr] = o[attr];
9664                     }
9665                 }
9666             }
9667             if(o.style){
9668                 Roo.DomHelper.applyStyles(el, o.style);
9669             }
9670             return this;
9671         },
9672
9673         /**
9674          * Convenience method for constructing a KeyMap
9675          * @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:
9676          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9677          * @param {Function} fn The function to call
9678          * @param {Object} scope (optional) The scope of the function
9679          * @return {Roo.KeyMap} The KeyMap created
9680          */
9681         addKeyListener : function(key, fn, scope){
9682             var config;
9683             if(typeof key != "object" || key instanceof Array){
9684                 config = {
9685                     key: key,
9686                     fn: fn,
9687                     scope: scope
9688                 };
9689             }else{
9690                 config = {
9691                     key : key.key,
9692                     shift : key.shift,
9693                     ctrl : key.ctrl,
9694                     alt : key.alt,
9695                     fn: fn,
9696                     scope: scope
9697                 };
9698             }
9699             return new Roo.KeyMap(this, config);
9700         },
9701
9702         /**
9703          * Creates a KeyMap for this element
9704          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9705          * @return {Roo.KeyMap} The KeyMap created
9706          */
9707         addKeyMap : function(config){
9708             return new Roo.KeyMap(this, config);
9709         },
9710
9711         /**
9712          * Returns true if this element is scrollable.
9713          * @return {Boolean}
9714          */
9715          isScrollable : function(){
9716             var dom = this.dom;
9717             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9718         },
9719
9720         /**
9721          * 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().
9722          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9723          * @param {Number} value The new scroll value
9724          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9725          * @return {Element} this
9726          */
9727
9728         scrollTo : function(side, value, animate){
9729             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9730             if(!animate || !A){
9731                 this.dom[prop] = value;
9732             }else{
9733                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9734                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9735             }
9736             return this;
9737         },
9738
9739         /**
9740          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9741          * within this element's scrollable range.
9742          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9743          * @param {Number} distance How far to scroll the element in pixels
9744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9745          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9746          * was scrolled as far as it could go.
9747          */
9748          scroll : function(direction, distance, animate){
9749              if(!this.isScrollable()){
9750                  return;
9751              }
9752              var el = this.dom;
9753              var l = el.scrollLeft, t = el.scrollTop;
9754              var w = el.scrollWidth, h = el.scrollHeight;
9755              var cw = el.clientWidth, ch = el.clientHeight;
9756              direction = direction.toLowerCase();
9757              var scrolled = false;
9758              var a = this.preanim(arguments, 2);
9759              switch(direction){
9760                  case "l":
9761                  case "left":
9762                      if(w - l > cw){
9763                          var v = Math.min(l + distance, w-cw);
9764                          this.scrollTo("left", v, a);
9765                          scrolled = true;
9766                      }
9767                      break;
9768                 case "r":
9769                 case "right":
9770                      if(l > 0){
9771                          var v = Math.max(l - distance, 0);
9772                          this.scrollTo("left", v, a);
9773                          scrolled = true;
9774                      }
9775                      break;
9776                 case "t":
9777                 case "top":
9778                 case "up":
9779                      if(t > 0){
9780                          var v = Math.max(t - distance, 0);
9781                          this.scrollTo("top", v, a);
9782                          scrolled = true;
9783                      }
9784                      break;
9785                 case "b":
9786                 case "bottom":
9787                 case "down":
9788                      if(h - t > ch){
9789                          var v = Math.min(t + distance, h-ch);
9790                          this.scrollTo("top", v, a);
9791                          scrolled = true;
9792                      }
9793                      break;
9794              }
9795              return scrolled;
9796         },
9797
9798         /**
9799          * Translates the passed page coordinates into left/top css values for this element
9800          * @param {Number/Array} x The page x or an array containing [x, y]
9801          * @param {Number} y The page y
9802          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9803          */
9804         translatePoints : function(x, y){
9805             if(typeof x == 'object' || x instanceof Array){
9806                 y = x[1]; x = x[0];
9807             }
9808             var p = this.getStyle('position');
9809             var o = this.getXY();
9810
9811             var l = parseInt(this.getStyle('left'), 10);
9812             var t = parseInt(this.getStyle('top'), 10);
9813
9814             if(isNaN(l)){
9815                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9816             }
9817             if(isNaN(t)){
9818                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9819             }
9820
9821             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9822         },
9823
9824         /**
9825          * Returns the current scroll position of the element.
9826          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9827          */
9828         getScroll : function(){
9829             var d = this.dom, doc = document;
9830             if(d == doc || d == doc.body){
9831                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9832                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9833                 return {left: l, top: t};
9834             }else{
9835                 return {left: d.scrollLeft, top: d.scrollTop};
9836             }
9837         },
9838
9839         /**
9840          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9841          * are convert to standard 6 digit hex color.
9842          * @param {String} attr The css attribute
9843          * @param {String} defaultValue The default value to use when a valid color isn't found
9844          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9845          * YUI color anims.
9846          */
9847         getColor : function(attr, defaultValue, prefix){
9848             var v = this.getStyle(attr);
9849             if(!v || v == "transparent" || v == "inherit") {
9850                 return defaultValue;
9851             }
9852             var color = typeof prefix == "undefined" ? "#" : prefix;
9853             if(v.substr(0, 4) == "rgb("){
9854                 var rvs = v.slice(4, v.length -1).split(",");
9855                 for(var i = 0; i < 3; i++){
9856                     var h = parseInt(rvs[i]).toString(16);
9857                     if(h < 16){
9858                         h = "0" + h;
9859                     }
9860                     color += h;
9861                 }
9862             } else {
9863                 if(v.substr(0, 1) == "#"){
9864                     if(v.length == 4) {
9865                         for(var i = 1; i < 4; i++){
9866                             var c = v.charAt(i);
9867                             color +=  c + c;
9868                         }
9869                     }else if(v.length == 7){
9870                         color += v.substr(1);
9871                     }
9872                 }
9873             }
9874             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9875         },
9876
9877         /**
9878          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9879          * gradient background, rounded corners and a 4-way shadow.
9880          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9881          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9882          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9883          * @return {Roo.Element} this
9884          */
9885         boxWrap : function(cls){
9886             cls = cls || 'x-box';
9887             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9888             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9889             return el;
9890         },
9891
9892         /**
9893          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9894          * @param {String} namespace The namespace in which to look for the attribute
9895          * @param {String} name The attribute name
9896          * @return {String} The attribute value
9897          */
9898         getAttributeNS : Roo.isIE ? function(ns, name){
9899             var d = this.dom;
9900             var type = typeof d[ns+":"+name];
9901             if(type != 'undefined' && type != 'unknown'){
9902                 return d[ns+":"+name];
9903             }
9904             return d[name];
9905         } : function(ns, name){
9906             var d = this.dom;
9907             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9908         },
9909         
9910         
9911         /**
9912          * Sets or Returns the value the dom attribute value
9913          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9914          * @param {String} value (optional) The value to set the attribute to
9915          * @return {String} The attribute value
9916          */
9917         attr : function(name){
9918             if (arguments.length > 1) {
9919                 this.dom.setAttribute(name, arguments[1]);
9920                 return arguments[1];
9921             }
9922             if (typeof(name) == 'object') {
9923                 for(var i in name) {
9924                     this.attr(i, name[i]);
9925                 }
9926                 return name;
9927             }
9928             
9929             
9930             if (!this.dom.hasAttribute(name)) {
9931                 return undefined;
9932             }
9933             return this.dom.getAttribute(name);
9934         }
9935         
9936         
9937         
9938     };
9939
9940     var ep = El.prototype;
9941
9942     /**
9943      * Appends an event handler (Shorthand for addListener)
9944      * @param {String}   eventName     The type of event to append
9945      * @param {Function} fn        The method the event invokes
9946      * @param {Object} scope       (optional) The scope (this object) of the fn
9947      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9948      * @method
9949      */
9950     ep.on = ep.addListener;
9951         // backwards compat
9952     ep.mon = ep.addListener;
9953
9954     /**
9955      * Removes an event handler from this element (shorthand for removeListener)
9956      * @param {String} eventName the type of event to remove
9957      * @param {Function} fn the method the event invokes
9958      * @return {Roo.Element} this
9959      * @method
9960      */
9961     ep.un = ep.removeListener;
9962
9963     /**
9964      * true to automatically adjust width and height settings for box-model issues (default to true)
9965      */
9966     ep.autoBoxAdjust = true;
9967
9968     // private
9969     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9970
9971     // private
9972     El.addUnits = function(v, defaultUnit){
9973         if(v === "" || v == "auto"){
9974             return v;
9975         }
9976         if(v === undefined){
9977             return '';
9978         }
9979         if(typeof v == "number" || !El.unitPattern.test(v)){
9980             return v + (defaultUnit || 'px');
9981         }
9982         return v;
9983     };
9984
9985     // special markup used throughout Roo when box wrapping elements
9986     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>';
9987     /**
9988      * Visibility mode constant - Use visibility to hide element
9989      * @static
9990      * @type Number
9991      */
9992     El.VISIBILITY = 1;
9993     /**
9994      * Visibility mode constant - Use display to hide element
9995      * @static
9996      * @type Number
9997      */
9998     El.DISPLAY = 2;
9999
10000     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10001     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10002     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10003
10004
10005
10006     /**
10007      * @private
10008      */
10009     El.cache = {};
10010
10011     var docEl;
10012
10013     /**
10014      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10015      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10016      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10017      * @return {Element} The Element object
10018      * @static
10019      */
10020     El.get = function(el){
10021         var ex, elm, id;
10022         if(!el){ return null; }
10023         if(typeof el == "string"){ // element id
10024             if(!(elm = document.getElementById(el))){
10025                 return null;
10026             }
10027             if(ex = El.cache[el]){
10028                 ex.dom = elm;
10029             }else{
10030                 ex = El.cache[el] = new El(elm);
10031             }
10032             return ex;
10033         }else if(el.tagName){ // dom element
10034             if(!(id = el.id)){
10035                 id = Roo.id(el);
10036             }
10037             if(ex = El.cache[id]){
10038                 ex.dom = el;
10039             }else{
10040                 ex = El.cache[id] = new El(el);
10041             }
10042             return ex;
10043         }else if(el instanceof El){
10044             if(el != docEl){
10045                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10046                                                               // catch case where it hasn't been appended
10047                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10048             }
10049             return el;
10050         }else if(el.isComposite){
10051             return el;
10052         }else if(el instanceof Array){
10053             return El.select(el);
10054         }else if(el == document){
10055             // create a bogus element object representing the document object
10056             if(!docEl){
10057                 var f = function(){};
10058                 f.prototype = El.prototype;
10059                 docEl = new f();
10060                 docEl.dom = document;
10061             }
10062             return docEl;
10063         }
10064         return null;
10065     };
10066
10067     // private
10068     El.uncache = function(el){
10069         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10070             if(a[i]){
10071                 delete El.cache[a[i].id || a[i]];
10072             }
10073         }
10074     };
10075
10076     // private
10077     // Garbage collection - uncache elements/purge listeners on orphaned elements
10078     // so we don't hold a reference and cause the browser to retain them
10079     El.garbageCollect = function(){
10080         if(!Roo.enableGarbageCollector){
10081             clearInterval(El.collectorThread);
10082             return;
10083         }
10084         for(var eid in El.cache){
10085             var el = El.cache[eid], d = el.dom;
10086             // -------------------------------------------------------
10087             // Determining what is garbage:
10088             // -------------------------------------------------------
10089             // !d
10090             // dom node is null, definitely garbage
10091             // -------------------------------------------------------
10092             // !d.parentNode
10093             // no parentNode == direct orphan, definitely garbage
10094             // -------------------------------------------------------
10095             // !d.offsetParent && !document.getElementById(eid)
10096             // display none elements have no offsetParent so we will
10097             // also try to look it up by it's id. However, check
10098             // offsetParent first so we don't do unneeded lookups.
10099             // This enables collection of elements that are not orphans
10100             // directly, but somewhere up the line they have an orphan
10101             // parent.
10102             // -------------------------------------------------------
10103             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10104                 delete El.cache[eid];
10105                 if(d && Roo.enableListenerCollection){
10106                     E.purgeElement(d);
10107                 }
10108             }
10109         }
10110     }
10111     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10112
10113
10114     // dom is optional
10115     El.Flyweight = function(dom){
10116         this.dom = dom;
10117     };
10118     El.Flyweight.prototype = El.prototype;
10119
10120     El._flyweights = {};
10121     /**
10122      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10123      * the dom node can be overwritten by other code.
10124      * @param {String/HTMLElement} el The dom node or id
10125      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10126      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10127      * @static
10128      * @return {Element} The shared Element object
10129      */
10130     El.fly = function(el, named){
10131         named = named || '_global';
10132         el = Roo.getDom(el);
10133         if(!el){
10134             return null;
10135         }
10136         if(!El._flyweights[named]){
10137             El._flyweights[named] = new El.Flyweight();
10138         }
10139         El._flyweights[named].dom = el;
10140         return El._flyweights[named];
10141     };
10142
10143     /**
10144      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10145      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10146      * Shorthand of {@link Roo.Element#get}
10147      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10148      * @return {Element} The Element object
10149      * @member Roo
10150      * @method get
10151      */
10152     Roo.get = El.get;
10153     /**
10154      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10155      * the dom node can be overwritten by other code.
10156      * Shorthand of {@link Roo.Element#fly}
10157      * @param {String/HTMLElement} el The dom node or id
10158      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10159      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10160      * @static
10161      * @return {Element} The shared Element object
10162      * @member Roo
10163      * @method fly
10164      */
10165     Roo.fly = El.fly;
10166
10167     // speedy lookup for elements never to box adjust
10168     var noBoxAdjust = Roo.isStrict ? {
10169         select:1
10170     } : {
10171         input:1, select:1, textarea:1
10172     };
10173     if(Roo.isIE || Roo.isGecko){
10174         noBoxAdjust['button'] = 1;
10175     }
10176
10177
10178     Roo.EventManager.on(window, 'unload', function(){
10179         delete El.cache;
10180         delete El._flyweights;
10181     });
10182 })();
10183
10184
10185
10186
10187 if(Roo.DomQuery){
10188     Roo.Element.selectorFunction = Roo.DomQuery.select;
10189 }
10190
10191 Roo.Element.select = function(selector, unique, root){
10192     var els;
10193     if(typeof selector == "string"){
10194         els = Roo.Element.selectorFunction(selector, root);
10195     }else if(selector.length !== undefined){
10196         els = selector;
10197     }else{
10198         throw "Invalid selector";
10199     }
10200     if(unique === true){
10201         return new Roo.CompositeElement(els);
10202     }else{
10203         return new Roo.CompositeElementLite(els);
10204     }
10205 };
10206 /**
10207  * Selects elements based on the passed CSS selector to enable working on them as 1.
10208  * @param {String/Array} selector The CSS selector or an array of elements
10209  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10210  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10211  * @return {CompositeElementLite/CompositeElement}
10212  * @member Roo
10213  * @method select
10214  */
10215 Roo.select = Roo.Element.select;
10216
10217
10218
10219
10220
10221
10222
10223
10224
10225
10226
10227
10228
10229
10230 /*
10231  * Based on:
10232  * Ext JS Library 1.1.1
10233  * Copyright(c) 2006-2007, Ext JS, LLC.
10234  *
10235  * Originally Released Under LGPL - original licence link has changed is not relivant.
10236  *
10237  * Fork - LGPL
10238  * <script type="text/javascript">
10239  */
10240
10241
10242
10243 //Notifies Element that fx methods are available
10244 Roo.enableFx = true;
10245
10246 /**
10247  * @class Roo.Fx
10248  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10249  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10250  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10251  * Element effects to work.</p><br/>
10252  *
10253  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10254  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10255  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10256  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10257  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10258  * expected results and should be done with care.</p><br/>
10259  *
10260  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10261  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10262 <pre>
10263 Value  Description
10264 -----  -----------------------------
10265 tl     The top left corner
10266 t      The center of the top edge
10267 tr     The top right corner
10268 l      The center of the left edge
10269 r      The center of the right edge
10270 bl     The bottom left corner
10271 b      The center of the bottom edge
10272 br     The bottom right corner
10273 </pre>
10274  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10275  * below are common options that can be passed to any Fx method.</b>
10276  * @cfg {Function} callback A function called when the effect is finished
10277  * @cfg {Object} scope The scope of the effect function
10278  * @cfg {String} easing A valid Easing value for the effect
10279  * @cfg {String} afterCls A css class to apply after the effect
10280  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10281  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10282  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10283  * effects that end with the element being visually hidden, ignored otherwise)
10284  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10285  * a function which returns such a specification that will be applied to the Element after the effect finishes
10286  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10287  * @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
10288  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10289  */
10290 Roo.Fx = {
10291         /**
10292          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10293          * origin for the slide effect.  This function automatically handles wrapping the element with
10294          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10295          * Usage:
10296          *<pre><code>
10297 // default: slide the element in from the top
10298 el.slideIn();
10299
10300 // custom: slide the element in from the right with a 2-second duration
10301 el.slideIn('r', { duration: 2 });
10302
10303 // common config options shown with default values
10304 el.slideIn('t', {
10305     easing: 'easeOut',
10306     duration: .5
10307 });
10308 </code></pre>
10309          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10310          * @param {Object} options (optional) Object literal with any of the Fx config options
10311          * @return {Roo.Element} The Element
10312          */
10313     slideIn : function(anchor, o){
10314         var el = this.getFxEl();
10315         o = o || {};
10316
10317         el.queueFx(o, function(){
10318
10319             anchor = anchor || "t";
10320
10321             // fix display to visibility
10322             this.fixDisplay();
10323
10324             // restore values after effect
10325             var r = this.getFxRestore();
10326             var b = this.getBox();
10327             // fixed size for slide
10328             this.setSize(b);
10329
10330             // wrap if needed
10331             var wrap = this.fxWrap(r.pos, o, "hidden");
10332
10333             var st = this.dom.style;
10334             st.visibility = "visible";
10335             st.position = "absolute";
10336
10337             // clear out temp styles after slide and unwrap
10338             var after = function(){
10339                 el.fxUnwrap(wrap, r.pos, o);
10340                 st.width = r.width;
10341                 st.height = r.height;
10342                 el.afterFx(o);
10343             };
10344             // time to calc the positions
10345             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10346
10347             switch(anchor.toLowerCase()){
10348                 case "t":
10349                     wrap.setSize(b.width, 0);
10350                     st.left = st.bottom = "0";
10351                     a = {height: bh};
10352                 break;
10353                 case "l":
10354                     wrap.setSize(0, b.height);
10355                     st.right = st.top = "0";
10356                     a = {width: bw};
10357                 break;
10358                 case "r":
10359                     wrap.setSize(0, b.height);
10360                     wrap.setX(b.right);
10361                     st.left = st.top = "0";
10362                     a = {width: bw, points: pt};
10363                 break;
10364                 case "b":
10365                     wrap.setSize(b.width, 0);
10366                     wrap.setY(b.bottom);
10367                     st.left = st.top = "0";
10368                     a = {height: bh, points: pt};
10369                 break;
10370                 case "tl":
10371                     wrap.setSize(0, 0);
10372                     st.right = st.bottom = "0";
10373                     a = {width: bw, height: bh};
10374                 break;
10375                 case "bl":
10376                     wrap.setSize(0, 0);
10377                     wrap.setY(b.y+b.height);
10378                     st.right = st.top = "0";
10379                     a = {width: bw, height: bh, points: pt};
10380                 break;
10381                 case "br":
10382                     wrap.setSize(0, 0);
10383                     wrap.setXY([b.right, b.bottom]);
10384                     st.left = st.top = "0";
10385                     a = {width: bw, height: bh, points: pt};
10386                 break;
10387                 case "tr":
10388                     wrap.setSize(0, 0);
10389                     wrap.setX(b.x+b.width);
10390                     st.left = st.bottom = "0";
10391                     a = {width: bw, height: bh, points: pt};
10392                 break;
10393             }
10394             this.dom.style.visibility = "visible";
10395             wrap.show();
10396
10397             arguments.callee.anim = wrap.fxanim(a,
10398                 o,
10399                 'motion',
10400                 .5,
10401                 'easeOut', after);
10402         });
10403         return this;
10404     },
10405     
10406         /**
10407          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10408          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10409          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10410          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10411          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10412          * Usage:
10413          *<pre><code>
10414 // default: slide the element out to the top
10415 el.slideOut();
10416
10417 // custom: slide the element out to the right with a 2-second duration
10418 el.slideOut('r', { duration: 2 });
10419
10420 // common config options shown with default values
10421 el.slideOut('t', {
10422     easing: 'easeOut',
10423     duration: .5,
10424     remove: false,
10425     useDisplay: false
10426 });
10427 </code></pre>
10428          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10429          * @param {Object} options (optional) Object literal with any of the Fx config options
10430          * @return {Roo.Element} The Element
10431          */
10432     slideOut : function(anchor, o){
10433         var el = this.getFxEl();
10434         o = o || {};
10435
10436         el.queueFx(o, function(){
10437
10438             anchor = anchor || "t";
10439
10440             // restore values after effect
10441             var r = this.getFxRestore();
10442             
10443             var b = this.getBox();
10444             // fixed size for slide
10445             this.setSize(b);
10446
10447             // wrap if needed
10448             var wrap = this.fxWrap(r.pos, o, "visible");
10449
10450             var st = this.dom.style;
10451             st.visibility = "visible";
10452             st.position = "absolute";
10453
10454             wrap.setSize(b);
10455
10456             var after = function(){
10457                 if(o.useDisplay){
10458                     el.setDisplayed(false);
10459                 }else{
10460                     el.hide();
10461                 }
10462
10463                 el.fxUnwrap(wrap, r.pos, o);
10464
10465                 st.width = r.width;
10466                 st.height = r.height;
10467
10468                 el.afterFx(o);
10469             };
10470
10471             var a, zero = {to: 0};
10472             switch(anchor.toLowerCase()){
10473                 case "t":
10474                     st.left = st.bottom = "0";
10475                     a = {height: zero};
10476                 break;
10477                 case "l":
10478                     st.right = st.top = "0";
10479                     a = {width: zero};
10480                 break;
10481                 case "r":
10482                     st.left = st.top = "0";
10483                     a = {width: zero, points: {to:[b.right, b.y]}};
10484                 break;
10485                 case "b":
10486                     st.left = st.top = "0";
10487                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10488                 break;
10489                 case "tl":
10490                     st.right = st.bottom = "0";
10491                     a = {width: zero, height: zero};
10492                 break;
10493                 case "bl":
10494                     st.right = st.top = "0";
10495                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10496                 break;
10497                 case "br":
10498                     st.left = st.top = "0";
10499                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10500                 break;
10501                 case "tr":
10502                     st.left = st.bottom = "0";
10503                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10504                 break;
10505             }
10506
10507             arguments.callee.anim = wrap.fxanim(a,
10508                 o,
10509                 'motion',
10510                 .5,
10511                 "easeOut", after);
10512         });
10513         return this;
10514     },
10515
10516         /**
10517          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10518          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10519          * The element must be removed from the DOM using the 'remove' config option if desired.
10520          * Usage:
10521          *<pre><code>
10522 // default
10523 el.puff();
10524
10525 // common config options shown with default values
10526 el.puff({
10527     easing: 'easeOut',
10528     duration: .5,
10529     remove: false,
10530     useDisplay: false
10531 });
10532 </code></pre>
10533          * @param {Object} options (optional) Object literal with any of the Fx config options
10534          * @return {Roo.Element} The Element
10535          */
10536     puff : function(o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539
10540         el.queueFx(o, function(){
10541             this.clearOpacity();
10542             this.show();
10543
10544             // restore values after effect
10545             var r = this.getFxRestore();
10546             var st = this.dom.style;
10547
10548             var after = function(){
10549                 if(o.useDisplay){
10550                     el.setDisplayed(false);
10551                 }else{
10552                     el.hide();
10553                 }
10554
10555                 el.clearOpacity();
10556
10557                 el.setPositioning(r.pos);
10558                 st.width = r.width;
10559                 st.height = r.height;
10560                 st.fontSize = '';
10561                 el.afterFx(o);
10562             };
10563
10564             var width = this.getWidth();
10565             var height = this.getHeight();
10566
10567             arguments.callee.anim = this.fxanim({
10568                     width : {to: this.adjustWidth(width * 2)},
10569                     height : {to: this.adjustHeight(height * 2)},
10570                     points : {by: [-(width * .5), -(height * .5)]},
10571                     opacity : {to: 0},
10572                     fontSize: {to:200, unit: "%"}
10573                 },
10574                 o,
10575                 'motion',
10576                 .5,
10577                 "easeOut", after);
10578         });
10579         return this;
10580     },
10581
10582         /**
10583          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10584          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10585          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10586          * Usage:
10587          *<pre><code>
10588 // default
10589 el.switchOff();
10590
10591 // all config options shown with default values
10592 el.switchOff({
10593     easing: 'easeIn',
10594     duration: .3,
10595     remove: false,
10596     useDisplay: false
10597 });
10598 </code></pre>
10599          * @param {Object} options (optional) Object literal with any of the Fx config options
10600          * @return {Roo.Element} The Element
10601          */
10602     switchOff : function(o){
10603         var el = this.getFxEl();
10604         o = o || {};
10605
10606         el.queueFx(o, function(){
10607             this.clearOpacity();
10608             this.clip();
10609
10610             // restore values after effect
10611             var r = this.getFxRestore();
10612             var st = this.dom.style;
10613
10614             var after = function(){
10615                 if(o.useDisplay){
10616                     el.setDisplayed(false);
10617                 }else{
10618                     el.hide();
10619                 }
10620
10621                 el.clearOpacity();
10622                 el.setPositioning(r.pos);
10623                 st.width = r.width;
10624                 st.height = r.height;
10625
10626                 el.afterFx(o);
10627             };
10628
10629             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10630                 this.clearOpacity();
10631                 (function(){
10632                     this.fxanim({
10633                         height:{to:1},
10634                         points:{by:[0, this.getHeight() * .5]}
10635                     }, o, 'motion', 0.3, 'easeIn', after);
10636                 }).defer(100, this);
10637             });
10638         });
10639         return this;
10640     },
10641
10642     /**
10643      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10644      * changed using the "attr" config option) and then fading back to the original color. If no original
10645      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10646      * Usage:
10647 <pre><code>
10648 // default: highlight background to yellow
10649 el.highlight();
10650
10651 // custom: highlight foreground text to blue for 2 seconds
10652 el.highlight("0000ff", { attr: 'color', duration: 2 });
10653
10654 // common config options shown with default values
10655 el.highlight("ffff9c", {
10656     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10657     endColor: (current color) or "ffffff",
10658     easing: 'easeIn',
10659     duration: 1
10660 });
10661 </code></pre>
10662      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10663      * @param {Object} options (optional) Object literal with any of the Fx config options
10664      * @return {Roo.Element} The Element
10665      */ 
10666     highlight : function(color, o){
10667         var el = this.getFxEl();
10668         o = o || {};
10669
10670         el.queueFx(o, function(){
10671             color = color || "ffff9c";
10672             attr = o.attr || "backgroundColor";
10673
10674             this.clearOpacity();
10675             this.show();
10676
10677             var origColor = this.getColor(attr);
10678             var restoreColor = this.dom.style[attr];
10679             endColor = (o.endColor || origColor) || "ffffff";
10680
10681             var after = function(){
10682                 el.dom.style[attr] = restoreColor;
10683                 el.afterFx(o);
10684             };
10685
10686             var a = {};
10687             a[attr] = {from: color, to: endColor};
10688             arguments.callee.anim = this.fxanim(a,
10689                 o,
10690                 'color',
10691                 1,
10692                 'easeIn', after);
10693         });
10694         return this;
10695     },
10696
10697    /**
10698     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10699     * Usage:
10700 <pre><code>
10701 // default: a single light blue ripple
10702 el.frame();
10703
10704 // custom: 3 red ripples lasting 3 seconds total
10705 el.frame("ff0000", 3, { duration: 3 });
10706
10707 // common config options shown with default values
10708 el.frame("C3DAF9", 1, {
10709     duration: 1 //duration of entire animation (not each individual ripple)
10710     // Note: Easing is not configurable and will be ignored if included
10711 });
10712 </code></pre>
10713     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10714     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10715     * @param {Object} options (optional) Object literal with any of the Fx config options
10716     * @return {Roo.Element} The Element
10717     */
10718     frame : function(color, count, o){
10719         var el = this.getFxEl();
10720         o = o || {};
10721
10722         el.queueFx(o, function(){
10723             color = color || "#C3DAF9";
10724             if(color.length == 6){
10725                 color = "#" + color;
10726             }
10727             count = count || 1;
10728             duration = o.duration || 1;
10729             this.show();
10730
10731             var b = this.getBox();
10732             var animFn = function(){
10733                 var proxy = this.createProxy({
10734
10735                      style:{
10736                         visbility:"hidden",
10737                         position:"absolute",
10738                         "z-index":"35000", // yee haw
10739                         border:"0px solid " + color
10740                      }
10741                   });
10742                 var scale = Roo.isBorderBox ? 2 : 1;
10743                 proxy.animate({
10744                     top:{from:b.y, to:b.y - 20},
10745                     left:{from:b.x, to:b.x - 20},
10746                     borderWidth:{from:0, to:10},
10747                     opacity:{from:1, to:0},
10748                     height:{from:b.height, to:(b.height + (20*scale))},
10749                     width:{from:b.width, to:(b.width + (20*scale))}
10750                 }, duration, function(){
10751                     proxy.remove();
10752                 });
10753                 if(--count > 0){
10754                      animFn.defer((duration/2)*1000, this);
10755                 }else{
10756                     el.afterFx(o);
10757                 }
10758             };
10759             animFn.call(this);
10760         });
10761         return this;
10762     },
10763
10764    /**
10765     * Creates a pause before any subsequent queued effects begin.  If there are
10766     * no effects queued after the pause it will have no effect.
10767     * Usage:
10768 <pre><code>
10769 el.pause(1);
10770 </code></pre>
10771     * @param {Number} seconds The length of time to pause (in seconds)
10772     * @return {Roo.Element} The Element
10773     */
10774     pause : function(seconds){
10775         var el = this.getFxEl();
10776         var o = {};
10777
10778         el.queueFx(o, function(){
10779             setTimeout(function(){
10780                 el.afterFx(o);
10781             }, seconds * 1000);
10782         });
10783         return this;
10784     },
10785
10786    /**
10787     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10788     * using the "endOpacity" config option.
10789     * Usage:
10790 <pre><code>
10791 // default: fade in from opacity 0 to 100%
10792 el.fadeIn();
10793
10794 // custom: fade in from opacity 0 to 75% over 2 seconds
10795 el.fadeIn({ endOpacity: .75, duration: 2});
10796
10797 // common config options shown with default values
10798 el.fadeIn({
10799     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10800     easing: 'easeOut',
10801     duration: .5
10802 });
10803 </code></pre>
10804     * @param {Object} options (optional) Object literal with any of the Fx config options
10805     * @return {Roo.Element} The Element
10806     */
10807     fadeIn : function(o){
10808         var el = this.getFxEl();
10809         o = o || {};
10810         el.queueFx(o, function(){
10811             this.setOpacity(0);
10812             this.fixDisplay();
10813             this.dom.style.visibility = 'visible';
10814             var to = o.endOpacity || 1;
10815             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10816                 o, null, .5, "easeOut", function(){
10817                 if(to == 1){
10818                     this.clearOpacity();
10819                 }
10820                 el.afterFx(o);
10821             });
10822         });
10823         return this;
10824     },
10825
10826    /**
10827     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10828     * using the "endOpacity" config option.
10829     * Usage:
10830 <pre><code>
10831 // default: fade out from the element's current opacity to 0
10832 el.fadeOut();
10833
10834 // custom: fade out from the element's current opacity to 25% over 2 seconds
10835 el.fadeOut({ endOpacity: .25, duration: 2});
10836
10837 // common config options shown with default values
10838 el.fadeOut({
10839     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10840     easing: 'easeOut',
10841     duration: .5
10842     remove: false,
10843     useDisplay: false
10844 });
10845 </code></pre>
10846     * @param {Object} options (optional) Object literal with any of the Fx config options
10847     * @return {Roo.Element} The Element
10848     */
10849     fadeOut : function(o){
10850         var el = this.getFxEl();
10851         o = o || {};
10852         el.queueFx(o, function(){
10853             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10854                 o, null, .5, "easeOut", function(){
10855                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10856                      this.dom.style.display = "none";
10857                 }else{
10858                      this.dom.style.visibility = "hidden";
10859                 }
10860                 this.clearOpacity();
10861                 el.afterFx(o);
10862             });
10863         });
10864         return this;
10865     },
10866
10867    /**
10868     * Animates the transition of an element's dimensions from a starting height/width
10869     * to an ending height/width.
10870     * Usage:
10871 <pre><code>
10872 // change height and width to 100x100 pixels
10873 el.scale(100, 100);
10874
10875 // common config options shown with default values.  The height and width will default to
10876 // the element's existing values if passed as null.
10877 el.scale(
10878     [element's width],
10879     [element's height], {
10880     easing: 'easeOut',
10881     duration: .35
10882 });
10883 </code></pre>
10884     * @param {Number} width  The new width (pass undefined to keep the original width)
10885     * @param {Number} height  The new height (pass undefined to keep the original height)
10886     * @param {Object} options (optional) Object literal with any of the Fx config options
10887     * @return {Roo.Element} The Element
10888     */
10889     scale : function(w, h, o){
10890         this.shift(Roo.apply({}, o, {
10891             width: w,
10892             height: h
10893         }));
10894         return this;
10895     },
10896
10897    /**
10898     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10899     * Any of these properties not specified in the config object will not be changed.  This effect 
10900     * requires that at least one new dimension, position or opacity setting must be passed in on
10901     * the config object in order for the function to have any effect.
10902     * Usage:
10903 <pre><code>
10904 // slide the element horizontally to x position 200 while changing the height and opacity
10905 el.shift({ x: 200, height: 50, opacity: .8 });
10906
10907 // common config options shown with default values.
10908 el.shift({
10909     width: [element's width],
10910     height: [element's height],
10911     x: [element's x position],
10912     y: [element's y position],
10913     opacity: [element's opacity],
10914     easing: 'easeOut',
10915     duration: .35
10916 });
10917 </code></pre>
10918     * @param {Object} options  Object literal with any of the Fx config options
10919     * @return {Roo.Element} The Element
10920     */
10921     shift : function(o){
10922         var el = this.getFxEl();
10923         o = o || {};
10924         el.queueFx(o, function(){
10925             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10926             if(w !== undefined){
10927                 a.width = {to: this.adjustWidth(w)};
10928             }
10929             if(h !== undefined){
10930                 a.height = {to: this.adjustHeight(h)};
10931             }
10932             if(x !== undefined || y !== undefined){
10933                 a.points = {to: [
10934                     x !== undefined ? x : this.getX(),
10935                     y !== undefined ? y : this.getY()
10936                 ]};
10937             }
10938             if(op !== undefined){
10939                 a.opacity = {to: op};
10940             }
10941             if(o.xy !== undefined){
10942                 a.points = {to: o.xy};
10943             }
10944             arguments.callee.anim = this.fxanim(a,
10945                 o, 'motion', .35, "easeOut", function(){
10946                 el.afterFx(o);
10947             });
10948         });
10949         return this;
10950     },
10951
10952         /**
10953          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10954          * ending point of the effect.
10955          * Usage:
10956          *<pre><code>
10957 // default: slide the element downward while fading out
10958 el.ghost();
10959
10960 // custom: slide the element out to the right with a 2-second duration
10961 el.ghost('r', { duration: 2 });
10962
10963 // common config options shown with default values
10964 el.ghost('b', {
10965     easing: 'easeOut',
10966     duration: .5
10967     remove: false,
10968     useDisplay: false
10969 });
10970 </code></pre>
10971          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10972          * @param {Object} options (optional) Object literal with any of the Fx config options
10973          * @return {Roo.Element} The Element
10974          */
10975     ghost : function(anchor, o){
10976         var el = this.getFxEl();
10977         o = o || {};
10978
10979         el.queueFx(o, function(){
10980             anchor = anchor || "b";
10981
10982             // restore values after effect
10983             var r = this.getFxRestore();
10984             var w = this.getWidth(),
10985                 h = this.getHeight();
10986
10987             var st = this.dom.style;
10988
10989             var after = function(){
10990                 if(o.useDisplay){
10991                     el.setDisplayed(false);
10992                 }else{
10993                     el.hide();
10994                 }
10995
10996                 el.clearOpacity();
10997                 el.setPositioning(r.pos);
10998                 st.width = r.width;
10999                 st.height = r.height;
11000
11001                 el.afterFx(o);
11002             };
11003
11004             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11005             switch(anchor.toLowerCase()){
11006                 case "t":
11007                     pt.by = [0, -h];
11008                 break;
11009                 case "l":
11010                     pt.by = [-w, 0];
11011                 break;
11012                 case "r":
11013                     pt.by = [w, 0];
11014                 break;
11015                 case "b":
11016                     pt.by = [0, h];
11017                 break;
11018                 case "tl":
11019                     pt.by = [-w, -h];
11020                 break;
11021                 case "bl":
11022                     pt.by = [-w, h];
11023                 break;
11024                 case "br":
11025                     pt.by = [w, h];
11026                 break;
11027                 case "tr":
11028                     pt.by = [w, -h];
11029                 break;
11030             }
11031
11032             arguments.callee.anim = this.fxanim(a,
11033                 o,
11034                 'motion',
11035                 .5,
11036                 "easeOut", after);
11037         });
11038         return this;
11039     },
11040
11041         /**
11042          * Ensures that all effects queued after syncFx is called on the element are
11043          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11044          * @return {Roo.Element} The Element
11045          */
11046     syncFx : function(){
11047         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11048             block : false,
11049             concurrent : true,
11050             stopFx : false
11051         });
11052         return this;
11053     },
11054
11055         /**
11056          * Ensures that all effects queued after sequenceFx is called on the element are
11057          * run in sequence.  This is the opposite of {@link #syncFx}.
11058          * @return {Roo.Element} The Element
11059          */
11060     sequenceFx : function(){
11061         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11062             block : false,
11063             concurrent : false,
11064             stopFx : false
11065         });
11066         return this;
11067     },
11068
11069         /* @private */
11070     nextFx : function(){
11071         var ef = this.fxQueue[0];
11072         if(ef){
11073             ef.call(this);
11074         }
11075     },
11076
11077         /**
11078          * Returns true if the element has any effects actively running or queued, else returns false.
11079          * @return {Boolean} True if element has active effects, else false
11080          */
11081     hasActiveFx : function(){
11082         return this.fxQueue && this.fxQueue[0];
11083     },
11084
11085         /**
11086          * Stops any running effects and clears the element's internal effects queue if it contains
11087          * any additional effects that haven't started yet.
11088          * @return {Roo.Element} The Element
11089          */
11090     stopFx : function(){
11091         if(this.hasActiveFx()){
11092             var cur = this.fxQueue[0];
11093             if(cur && cur.anim && cur.anim.isAnimated()){
11094                 this.fxQueue = [cur]; // clear out others
11095                 cur.anim.stop(true);
11096             }
11097         }
11098         return this;
11099     },
11100
11101         /* @private */
11102     beforeFx : function(o){
11103         if(this.hasActiveFx() && !o.concurrent){
11104            if(o.stopFx){
11105                this.stopFx();
11106                return true;
11107            }
11108            return false;
11109         }
11110         return true;
11111     },
11112
11113         /**
11114          * Returns true if the element is currently blocking so that no other effect can be queued
11115          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11116          * used to ensure that an effect initiated by a user action runs to completion prior to the
11117          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11118          * @return {Boolean} True if blocking, else false
11119          */
11120     hasFxBlock : function(){
11121         var q = this.fxQueue;
11122         return q && q[0] && q[0].block;
11123     },
11124
11125         /* @private */
11126     queueFx : function(o, fn){
11127         if(!this.fxQueue){
11128             this.fxQueue = [];
11129         }
11130         if(!this.hasFxBlock()){
11131             Roo.applyIf(o, this.fxDefaults);
11132             if(!o.concurrent){
11133                 var run = this.beforeFx(o);
11134                 fn.block = o.block;
11135                 this.fxQueue.push(fn);
11136                 if(run){
11137                     this.nextFx();
11138                 }
11139             }else{
11140                 fn.call(this);
11141             }
11142         }
11143         return this;
11144     },
11145
11146         /* @private */
11147     fxWrap : function(pos, o, vis){
11148         var wrap;
11149         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11150             var wrapXY;
11151             if(o.fixPosition){
11152                 wrapXY = this.getXY();
11153             }
11154             var div = document.createElement("div");
11155             div.style.visibility = vis;
11156             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11157             wrap.setPositioning(pos);
11158             if(wrap.getStyle("position") == "static"){
11159                 wrap.position("relative");
11160             }
11161             this.clearPositioning('auto');
11162             wrap.clip();
11163             wrap.dom.appendChild(this.dom);
11164             if(wrapXY){
11165                 wrap.setXY(wrapXY);
11166             }
11167         }
11168         return wrap;
11169     },
11170
11171         /* @private */
11172     fxUnwrap : function(wrap, pos, o){
11173         this.clearPositioning();
11174         this.setPositioning(pos);
11175         if(!o.wrap){
11176             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11177             wrap.remove();
11178         }
11179     },
11180
11181         /* @private */
11182     getFxRestore : function(){
11183         var st = this.dom.style;
11184         return {pos: this.getPositioning(), width: st.width, height : st.height};
11185     },
11186
11187         /* @private */
11188     afterFx : function(o){
11189         if(o.afterStyle){
11190             this.applyStyles(o.afterStyle);
11191         }
11192         if(o.afterCls){
11193             this.addClass(o.afterCls);
11194         }
11195         if(o.remove === true){
11196             this.remove();
11197         }
11198         Roo.callback(o.callback, o.scope, [this]);
11199         if(!o.concurrent){
11200             this.fxQueue.shift();
11201             this.nextFx();
11202         }
11203     },
11204
11205         /* @private */
11206     getFxEl : function(){ // support for composite element fx
11207         return Roo.get(this.dom);
11208     },
11209
11210         /* @private */
11211     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11212         animType = animType || 'run';
11213         opt = opt || {};
11214         var anim = Roo.lib.Anim[animType](
11215             this.dom, args,
11216             (opt.duration || defaultDur) || .35,
11217             (opt.easing || defaultEase) || 'easeOut',
11218             function(){
11219                 Roo.callback(cb, this);
11220             },
11221             this
11222         );
11223         opt.anim = anim;
11224         return anim;
11225     }
11226 };
11227
11228 // backwords compat
11229 Roo.Fx.resize = Roo.Fx.scale;
11230
11231 //When included, Roo.Fx is automatically applied to Element so that all basic
11232 //effects are available directly via the Element API
11233 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11234  * Based on:
11235  * Ext JS Library 1.1.1
11236  * Copyright(c) 2006-2007, Ext JS, LLC.
11237  *
11238  * Originally Released Under LGPL - original licence link has changed is not relivant.
11239  *
11240  * Fork - LGPL
11241  * <script type="text/javascript">
11242  */
11243
11244
11245 /**
11246  * @class Roo.CompositeElement
11247  * Standard composite class. Creates a Roo.Element for every element in the collection.
11248  * <br><br>
11249  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11250  * actions will be performed on all the elements in this collection.</b>
11251  * <br><br>
11252  * All methods return <i>this</i> and can be chained.
11253  <pre><code>
11254  var els = Roo.select("#some-el div.some-class", true);
11255  // or select directly from an existing element
11256  var el = Roo.get('some-el');
11257  el.select('div.some-class', true);
11258
11259  els.setWidth(100); // all elements become 100 width
11260  els.hide(true); // all elements fade out and hide
11261  // or
11262  els.setWidth(100).hide(true);
11263  </code></pre>
11264  */
11265 Roo.CompositeElement = function(els){
11266     this.elements = [];
11267     this.addElements(els);
11268 };
11269 Roo.CompositeElement.prototype = {
11270     isComposite: true,
11271     addElements : function(els){
11272         if(!els) {
11273             return this;
11274         }
11275         if(typeof els == "string"){
11276             els = Roo.Element.selectorFunction(els);
11277         }
11278         var yels = this.elements;
11279         var index = yels.length-1;
11280         for(var i = 0, len = els.length; i < len; i++) {
11281                 yels[++index] = Roo.get(els[i]);
11282         }
11283         return this;
11284     },
11285
11286     /**
11287     * Clears this composite and adds the elements returned by the passed selector.
11288     * @param {String/Array} els A string CSS selector, an array of elements or an element
11289     * @return {CompositeElement} this
11290     */
11291     fill : function(els){
11292         this.elements = [];
11293         this.add(els);
11294         return this;
11295     },
11296
11297     /**
11298     * Filters this composite to only elements that match the passed selector.
11299     * @param {String} selector A string CSS selector
11300     * @param {Boolean} inverse return inverse filter (not matches)
11301     * @return {CompositeElement} this
11302     */
11303     filter : function(selector, inverse){
11304         var els = [];
11305         inverse = inverse || false;
11306         this.each(function(el){
11307             var match = inverse ? !el.is(selector) : el.is(selector);
11308             if(match){
11309                 els[els.length] = el.dom;
11310             }
11311         });
11312         this.fill(els);
11313         return this;
11314     },
11315
11316     invoke : function(fn, args){
11317         var els = this.elements;
11318         for(var i = 0, len = els.length; i < len; i++) {
11319                 Roo.Element.prototype[fn].apply(els[i], args);
11320         }
11321         return this;
11322     },
11323     /**
11324     * Adds elements to this composite.
11325     * @param {String/Array} els A string CSS selector, an array of elements or an element
11326     * @return {CompositeElement} this
11327     */
11328     add : function(els){
11329         if(typeof els == "string"){
11330             this.addElements(Roo.Element.selectorFunction(els));
11331         }else if(els.length !== undefined){
11332             this.addElements(els);
11333         }else{
11334             this.addElements([els]);
11335         }
11336         return this;
11337     },
11338     /**
11339     * Calls the passed function passing (el, this, index) for each element in this composite.
11340     * @param {Function} fn The function to call
11341     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11342     * @return {CompositeElement} this
11343     */
11344     each : function(fn, scope){
11345         var els = this.elements;
11346         for(var i = 0, len = els.length; i < len; i++){
11347             if(fn.call(scope || els[i], els[i], this, i) === false) {
11348                 break;
11349             }
11350         }
11351         return this;
11352     },
11353
11354     /**
11355      * Returns the Element object at the specified index
11356      * @param {Number} index
11357      * @return {Roo.Element}
11358      */
11359     item : function(index){
11360         return this.elements[index] || null;
11361     },
11362
11363     /**
11364      * Returns the first Element
11365      * @return {Roo.Element}
11366      */
11367     first : function(){
11368         return this.item(0);
11369     },
11370
11371     /**
11372      * Returns the last Element
11373      * @return {Roo.Element}
11374      */
11375     last : function(){
11376         return this.item(this.elements.length-1);
11377     },
11378
11379     /**
11380      * Returns the number of elements in this composite
11381      * @return Number
11382      */
11383     getCount : function(){
11384         return this.elements.length;
11385     },
11386
11387     /**
11388      * Returns true if this composite contains the passed element
11389      * @return Boolean
11390      */
11391     contains : function(el){
11392         return this.indexOf(el) !== -1;
11393     },
11394
11395     /**
11396      * Returns true if this composite contains the passed element
11397      * @return Boolean
11398      */
11399     indexOf : function(el){
11400         return this.elements.indexOf(Roo.get(el));
11401     },
11402
11403
11404     /**
11405     * Removes the specified element(s).
11406     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11407     * or an array of any of those.
11408     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11409     * @return {CompositeElement} this
11410     */
11411     removeElement : function(el, removeDom){
11412         if(el instanceof Array){
11413             for(var i = 0, len = el.length; i < len; i++){
11414                 this.removeElement(el[i]);
11415             }
11416             return this;
11417         }
11418         var index = typeof el == 'number' ? el : this.indexOf(el);
11419         if(index !== -1){
11420             if(removeDom){
11421                 var d = this.elements[index];
11422                 if(d.dom){
11423                     d.remove();
11424                 }else{
11425                     d.parentNode.removeChild(d);
11426                 }
11427             }
11428             this.elements.splice(index, 1);
11429         }
11430         return this;
11431     },
11432
11433     /**
11434     * Replaces the specified element with the passed element.
11435     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11436     * to replace.
11437     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11438     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11439     * @return {CompositeElement} this
11440     */
11441     replaceElement : function(el, replacement, domReplace){
11442         var index = typeof el == 'number' ? el : this.indexOf(el);
11443         if(index !== -1){
11444             if(domReplace){
11445                 this.elements[index].replaceWith(replacement);
11446             }else{
11447                 this.elements.splice(index, 1, Roo.get(replacement))
11448             }
11449         }
11450         return this;
11451     },
11452
11453     /**
11454      * Removes all elements.
11455      */
11456     clear : function(){
11457         this.elements = [];
11458     }
11459 };
11460 (function(){
11461     Roo.CompositeElement.createCall = function(proto, fnName){
11462         if(!proto[fnName]){
11463             proto[fnName] = function(){
11464                 return this.invoke(fnName, arguments);
11465             };
11466         }
11467     };
11468     for(var fnName in Roo.Element.prototype){
11469         if(typeof Roo.Element.prototype[fnName] == "function"){
11470             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11471         }
11472     };
11473 })();
11474 /*
11475  * Based on:
11476  * Ext JS Library 1.1.1
11477  * Copyright(c) 2006-2007, Ext JS, LLC.
11478  *
11479  * Originally Released Under LGPL - original licence link has changed is not relivant.
11480  *
11481  * Fork - LGPL
11482  * <script type="text/javascript">
11483  */
11484
11485 /**
11486  * @class Roo.CompositeElementLite
11487  * @extends Roo.CompositeElement
11488  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11489  <pre><code>
11490  var els = Roo.select("#some-el div.some-class");
11491  // or select directly from an existing element
11492  var el = Roo.get('some-el');
11493  el.select('div.some-class');
11494
11495  els.setWidth(100); // all elements become 100 width
11496  els.hide(true); // all elements fade out and hide
11497  // or
11498  els.setWidth(100).hide(true);
11499  </code></pre><br><br>
11500  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11501  * actions will be performed on all the elements in this collection.</b>
11502  */
11503 Roo.CompositeElementLite = function(els){
11504     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11505     this.el = new Roo.Element.Flyweight();
11506 };
11507 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11508     addElements : function(els){
11509         if(els){
11510             if(els instanceof Array){
11511                 this.elements = this.elements.concat(els);
11512             }else{
11513                 var yels = this.elements;
11514                 var index = yels.length-1;
11515                 for(var i = 0, len = els.length; i < len; i++) {
11516                     yels[++index] = els[i];
11517                 }
11518             }
11519         }
11520         return this;
11521     },
11522     invoke : function(fn, args){
11523         var els = this.elements;
11524         var el = this.el;
11525         for(var i = 0, len = els.length; i < len; i++) {
11526             el.dom = els[i];
11527                 Roo.Element.prototype[fn].apply(el, args);
11528         }
11529         return this;
11530     },
11531     /**
11532      * Returns a flyweight Element of the dom element object at the specified index
11533      * @param {Number} index
11534      * @return {Roo.Element}
11535      */
11536     item : function(index){
11537         if(!this.elements[index]){
11538             return null;
11539         }
11540         this.el.dom = this.elements[index];
11541         return this.el;
11542     },
11543
11544     // fixes scope with flyweight
11545     addListener : function(eventName, handler, scope, opt){
11546         var els = this.elements;
11547         for(var i = 0, len = els.length; i < len; i++) {
11548             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11549         }
11550         return this;
11551     },
11552
11553     /**
11554     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11555     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11556     * a reference to the dom node, use el.dom.</b>
11557     * @param {Function} fn The function to call
11558     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11559     * @return {CompositeElement} this
11560     */
11561     each : function(fn, scope){
11562         var els = this.elements;
11563         var el = this.el;
11564         for(var i = 0, len = els.length; i < len; i++){
11565             el.dom = els[i];
11566                 if(fn.call(scope || el, el, this, i) === false){
11567                 break;
11568             }
11569         }
11570         return this;
11571     },
11572
11573     indexOf : function(el){
11574         return this.elements.indexOf(Roo.getDom(el));
11575     },
11576
11577     replaceElement : function(el, replacement, domReplace){
11578         var index = typeof el == 'number' ? el : this.indexOf(el);
11579         if(index !== -1){
11580             replacement = Roo.getDom(replacement);
11581             if(domReplace){
11582                 var d = this.elements[index];
11583                 d.parentNode.insertBefore(replacement, d);
11584                 d.parentNode.removeChild(d);
11585             }
11586             this.elements.splice(index, 1, replacement);
11587         }
11588         return this;
11589     }
11590 });
11591 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11592
11593 /*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603
11604  
11605
11606 /**
11607  * @class Roo.data.Connection
11608  * @extends Roo.util.Observable
11609  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11610  * either to a configured URL, or to a URL specified at request time. 
11611  * 
11612  * Requests made by this class are asynchronous, and will return immediately. No data from
11613  * the server will be available to the statement immediately following the {@link #request} call.
11614  * To process returned data, use a callback in the request options object, or an event listener.
11615  * 
11616  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11617  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11618  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11619  * property and, if present, the IFRAME's XML document as the responseXML property.
11620  * 
11621  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11622  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11623  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11624  * standard DOM methods.
11625  * @constructor
11626  * @param {Object} config a configuration object.
11627  */
11628 Roo.data.Connection = function(config){
11629     Roo.apply(this, config);
11630     this.addEvents({
11631         /**
11632          * @event beforerequest
11633          * Fires before a network request is made to retrieve a data object.
11634          * @param {Connection} conn This Connection object.
11635          * @param {Object} options The options config object passed to the {@link #request} method.
11636          */
11637         "beforerequest" : true,
11638         /**
11639          * @event requestcomplete
11640          * Fires if the request was successfully completed.
11641          * @param {Connection} conn This Connection object.
11642          * @param {Object} response The XHR object containing the response data.
11643          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11644          * @param {Object} options The options config object passed to the {@link #request} method.
11645          */
11646         "requestcomplete" : true,
11647         /**
11648          * @event requestexception
11649          * Fires if an error HTTP status was returned from the server.
11650          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11651          * @param {Connection} conn This Connection object.
11652          * @param {Object} response The XHR object containing the response data.
11653          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11654          * @param {Object} options The options config object passed to the {@link #request} method.
11655          */
11656         "requestexception" : true
11657     });
11658     Roo.data.Connection.superclass.constructor.call(this);
11659 };
11660
11661 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11662     /**
11663      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11664      */
11665     /**
11666      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11667      * extra parameters to each request made by this object. (defaults to undefined)
11668      */
11669     /**
11670      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11671      *  to each request made by this object. (defaults to undefined)
11672      */
11673     /**
11674      * @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)
11675      */
11676     /**
11677      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11678      */
11679     timeout : 30000,
11680     /**
11681      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11682      * @type Boolean
11683      */
11684     autoAbort:false,
11685
11686     /**
11687      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11688      * @type Boolean
11689      */
11690     disableCaching: true,
11691
11692     /**
11693      * Sends an HTTP request to a remote server.
11694      * @param {Object} options An object which may contain the following properties:<ul>
11695      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11696      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11697      * request, a url encoded string or a function to call to get either.</li>
11698      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11699      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11700      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11701      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11702      * <li>options {Object} The parameter to the request call.</li>
11703      * <li>success {Boolean} True if the request succeeded.</li>
11704      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11705      * </ul></li>
11706      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11707      * The callback is passed the following parameters:<ul>
11708      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11709      * <li>options {Object} The parameter to the request call.</li>
11710      * </ul></li>
11711      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11712      * The callback is passed the following parameters:<ul>
11713      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11714      * <li>options {Object} The parameter to the request call.</li>
11715      * </ul></li>
11716      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11717      * for the callback function. Defaults to the browser window.</li>
11718      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11719      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11720      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11721      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11722      * params for the post data. Any params will be appended to the URL.</li>
11723      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11724      * </ul>
11725      * @return {Number} transactionId
11726      */
11727     request : function(o){
11728         if(this.fireEvent("beforerequest", this, o) !== false){
11729             var p = o.params;
11730
11731             if(typeof p == "function"){
11732                 p = p.call(o.scope||window, o);
11733             }
11734             if(typeof p == "object"){
11735                 p = Roo.urlEncode(o.params);
11736             }
11737             if(this.extraParams){
11738                 var extras = Roo.urlEncode(this.extraParams);
11739                 p = p ? (p + '&' + extras) : extras;
11740             }
11741
11742             var url = o.url || this.url;
11743             if(typeof url == 'function'){
11744                 url = url.call(o.scope||window, o);
11745             }
11746
11747             if(o.form){
11748                 var form = Roo.getDom(o.form);
11749                 url = url || form.action;
11750
11751                 var enctype = form.getAttribute("enctype");
11752                 
11753                 if (o.formData) {
11754                     return this.doFormDataUpload(o, url);
11755                 }
11756                 
11757                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11758                     return this.doFormUpload(o, p, url);
11759                 }
11760                 var f = Roo.lib.Ajax.serializeForm(form);
11761                 p = p ? (p + '&' + f) : f;
11762             }
11763             
11764             if (!o.form && o.formData) {
11765                 o.formData = o.formData === true ? new FormData() : o.formData;
11766                 for (var k in o.params) {
11767                     o.formData.append(k,o.params[k]);
11768                 }
11769                     
11770                 return this.doFormDataUpload(o, url);
11771             }
11772             
11773
11774             var hs = o.headers;
11775             if(this.defaultHeaders){
11776                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11777                 if(!o.headers){
11778                     o.headers = hs;
11779                 }
11780             }
11781
11782             var cb = {
11783                 success: this.handleResponse,
11784                 failure: this.handleFailure,
11785                 scope: this,
11786                 argument: {options: o},
11787                 timeout : o.timeout || this.timeout
11788             };
11789
11790             var method = o.method||this.method||(p ? "POST" : "GET");
11791
11792             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11793                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11794             }
11795
11796             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11797                 if(o.autoAbort){
11798                     this.abort();
11799                 }
11800             }else if(this.autoAbort !== false){
11801                 this.abort();
11802             }
11803
11804             if((method == 'GET' && p) || o.xmlData){
11805                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11806                 p = '';
11807             }
11808             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11809             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11810             Roo.lib.Ajax.useDefaultHeader == true;
11811             return this.transId;
11812         }else{
11813             Roo.callback(o.callback, o.scope, [o, null, null]);
11814             return null;
11815         }
11816     },
11817
11818     /**
11819      * Determine whether this object has a request outstanding.
11820      * @param {Number} transactionId (Optional) defaults to the last transaction
11821      * @return {Boolean} True if there is an outstanding request.
11822      */
11823     isLoading : function(transId){
11824         if(transId){
11825             return Roo.lib.Ajax.isCallInProgress(transId);
11826         }else{
11827             return this.transId ? true : false;
11828         }
11829     },
11830
11831     /**
11832      * Aborts any outstanding request.
11833      * @param {Number} transactionId (Optional) defaults to the last transaction
11834      */
11835     abort : function(transId){
11836         if(transId || this.isLoading()){
11837             Roo.lib.Ajax.abort(transId || this.transId);
11838         }
11839     },
11840
11841     // private
11842     handleResponse : function(response){
11843         this.transId = false;
11844         var options = response.argument.options;
11845         response.argument = options ? options.argument : null;
11846         this.fireEvent("requestcomplete", this, response, options);
11847         Roo.callback(options.success, options.scope, [response, options]);
11848         Roo.callback(options.callback, options.scope, [options, true, response]);
11849     },
11850
11851     // private
11852     handleFailure : function(response, e){
11853         this.transId = false;
11854         var options = response.argument.options;
11855         response.argument = options ? options.argument : null;
11856         this.fireEvent("requestexception", this, response, options, e);
11857         Roo.callback(options.failure, options.scope, [response, options]);
11858         Roo.callback(options.callback, options.scope, [options, false, response]);
11859     },
11860
11861     // private
11862     doFormUpload : function(o, ps, url){
11863         var id = Roo.id();
11864         var frame = document.createElement('iframe');
11865         frame.id = id;
11866         frame.name = id;
11867         frame.className = 'x-hidden';
11868         if(Roo.isIE){
11869             frame.src = Roo.SSL_SECURE_URL;
11870         }
11871         document.body.appendChild(frame);
11872
11873         if(Roo.isIE){
11874            document.frames[id].name = id;
11875         }
11876
11877         var form = Roo.getDom(o.form);
11878         form.target = id;
11879         form.method = 'POST';
11880         form.enctype = form.encoding = 'multipart/form-data';
11881         if(url){
11882             form.action = url;
11883         }
11884
11885         var hiddens, hd;
11886         if(ps){ // add dynamic params
11887             hiddens = [];
11888             ps = Roo.urlDecode(ps, false);
11889             for(var k in ps){
11890                 if(ps.hasOwnProperty(k)){
11891                     hd = document.createElement('input');
11892                     hd.type = 'hidden';
11893                     hd.name = k;
11894                     hd.value = ps[k];
11895                     form.appendChild(hd);
11896                     hiddens.push(hd);
11897                 }
11898             }
11899         }
11900
11901         function cb(){
11902             var r = {  // bogus response object
11903                 responseText : '',
11904                 responseXML : null
11905             };
11906
11907             r.argument = o ? o.argument : null;
11908
11909             try { //
11910                 var doc;
11911                 if(Roo.isIE){
11912                     doc = frame.contentWindow.document;
11913                 }else {
11914                     doc = (frame.contentDocument || window.frames[id].document);
11915                 }
11916                 if(doc && doc.body){
11917                     r.responseText = doc.body.innerHTML;
11918                 }
11919                 if(doc && doc.XMLDocument){
11920                     r.responseXML = doc.XMLDocument;
11921                 }else {
11922                     r.responseXML = doc;
11923                 }
11924             }
11925             catch(e) {
11926                 // ignore
11927             }
11928
11929             Roo.EventManager.removeListener(frame, 'load', cb, this);
11930
11931             this.fireEvent("requestcomplete", this, r, o);
11932             Roo.callback(o.success, o.scope, [r, o]);
11933             Roo.callback(o.callback, o.scope, [o, true, r]);
11934
11935             setTimeout(function(){document.body.removeChild(frame);}, 100);
11936         }
11937
11938         Roo.EventManager.on(frame, 'load', cb, this);
11939         form.submit();
11940
11941         if(hiddens){ // remove dynamic params
11942             for(var i = 0, len = hiddens.length; i < len; i++){
11943                 form.removeChild(hiddens[i]);
11944             }
11945         }
11946     },
11947     // this is a 'formdata version???'
11948     
11949     
11950     doFormDataUpload : function(o,  url)
11951     {
11952         var formData;
11953         if (o.form) {
11954             var form =  Roo.getDom(o.form);
11955             form.enctype = form.encoding = 'multipart/form-data';
11956             formData = o.formData === true ? new FormData(form) : o.formData;
11957         } else {
11958             formData = o.formData === true ? new FormData() : o.formData;
11959         }
11960         
11961       
11962         var cb = {
11963             success: this.handleResponse,
11964             failure: this.handleFailure,
11965             scope: this,
11966             argument: {options: o},
11967             timeout : o.timeout || this.timeout
11968         };
11969  
11970         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11971             if(o.autoAbort){
11972                 this.abort();
11973             }
11974         }else if(this.autoAbort !== false){
11975             this.abort();
11976         }
11977
11978         //Roo.lib.Ajax.defaultPostHeader = null;
11979         Roo.lib.Ajax.useDefaultHeader = false;
11980         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11981         Roo.lib.Ajax.useDefaultHeader = true;
11982  
11983          
11984     }
11985     
11986 });
11987 /*
11988  * Based on:
11989  * Ext JS Library 1.1.1
11990  * Copyright(c) 2006-2007, Ext JS, LLC.
11991  *
11992  * Originally Released Under LGPL - original licence link has changed is not relivant.
11993  *
11994  * Fork - LGPL
11995  * <script type="text/javascript">
11996  */
11997  
11998 /**
11999  * Global Ajax request class.
12000  * 
12001  * @class Roo.Ajax
12002  * @extends Roo.data.Connection
12003  * @static
12004  * 
12005  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12006  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12007  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12008  * @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)
12009  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12010  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12011  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12012  */
12013 Roo.Ajax = new Roo.data.Connection({
12014     // fix up the docs
12015     /**
12016      * @scope Roo.Ajax
12017      * @type {Boolear} 
12018      */
12019     autoAbort : false,
12020
12021     /**
12022      * Serialize the passed form into a url encoded string
12023      * @scope Roo.Ajax
12024      * @param {String/HTMLElement} form
12025      * @return {String}
12026      */
12027     serializeForm : function(form){
12028         return Roo.lib.Ajax.serializeForm(form);
12029     }
12030 });/*
12031  * Based on:
12032  * Ext JS Library 1.1.1
12033  * Copyright(c) 2006-2007, Ext JS, LLC.
12034  *
12035  * Originally Released Under LGPL - original licence link has changed is not relivant.
12036  *
12037  * Fork - LGPL
12038  * <script type="text/javascript">
12039  */
12040
12041  
12042 /**
12043  * @class Roo.UpdateManager
12044  * @extends Roo.util.Observable
12045  * Provides AJAX-style update for Element object.<br><br>
12046  * Usage:<br>
12047  * <pre><code>
12048  * // Get it from a Roo.Element object
12049  * var el = Roo.get("foo");
12050  * var mgr = el.getUpdateManager();
12051  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12052  * ...
12053  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12054  * <br>
12055  * // or directly (returns the same UpdateManager instance)
12056  * var mgr = new Roo.UpdateManager("myElementId");
12057  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12058  * mgr.on("update", myFcnNeedsToKnow);
12059  * <br>
12060    // short handed call directly from the element object
12061    Roo.get("foo").load({
12062         url: "bar.php",
12063         scripts:true,
12064         params: "for=bar",
12065         text: "Loading Foo..."
12066    });
12067  * </code></pre>
12068  * @constructor
12069  * Create new UpdateManager directly.
12070  * @param {String/HTMLElement/Roo.Element} el The element to update
12071  * @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).
12072  */
12073 Roo.UpdateManager = function(el, forceNew){
12074     el = Roo.get(el);
12075     if(!forceNew && el.updateManager){
12076         return el.updateManager;
12077     }
12078     /**
12079      * The Element object
12080      * @type Roo.Element
12081      */
12082     this.el = el;
12083     /**
12084      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12085      * @type String
12086      */
12087     this.defaultUrl = null;
12088
12089     this.addEvents({
12090         /**
12091          * @event beforeupdate
12092          * Fired before an update is made, return false from your handler and the update is cancelled.
12093          * @param {Roo.Element} el
12094          * @param {String/Object/Function} url
12095          * @param {String/Object} params
12096          */
12097         "beforeupdate": true,
12098         /**
12099          * @event update
12100          * Fired after successful update is made.
12101          * @param {Roo.Element} el
12102          * @param {Object} oResponseObject The response Object
12103          */
12104         "update": true,
12105         /**
12106          * @event failure
12107          * Fired on update failure.
12108          * @param {Roo.Element} el
12109          * @param {Object} oResponseObject The response Object
12110          */
12111         "failure": true
12112     });
12113     var d = Roo.UpdateManager.defaults;
12114     /**
12115      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12116      * @type String
12117      */
12118     this.sslBlankUrl = d.sslBlankUrl;
12119     /**
12120      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12121      * @type Boolean
12122      */
12123     this.disableCaching = d.disableCaching;
12124     /**
12125      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12126      * @type String
12127      */
12128     this.indicatorText = d.indicatorText;
12129     /**
12130      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12131      * @type String
12132      */
12133     this.showLoadIndicator = d.showLoadIndicator;
12134     /**
12135      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12136      * @type Number
12137      */
12138     this.timeout = d.timeout;
12139
12140     /**
12141      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12142      * @type Boolean
12143      */
12144     this.loadScripts = d.loadScripts;
12145
12146     /**
12147      * Transaction object of current executing transaction
12148      */
12149     this.transaction = null;
12150
12151     /**
12152      * @private
12153      */
12154     this.autoRefreshProcId = null;
12155     /**
12156      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12157      * @type Function
12158      */
12159     this.refreshDelegate = this.refresh.createDelegate(this);
12160     /**
12161      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12162      * @type Function
12163      */
12164     this.updateDelegate = this.update.createDelegate(this);
12165     /**
12166      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12167      * @type Function
12168      */
12169     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12170     /**
12171      * @private
12172      */
12173     this.successDelegate = this.processSuccess.createDelegate(this);
12174     /**
12175      * @private
12176      */
12177     this.failureDelegate = this.processFailure.createDelegate(this);
12178
12179     if(!this.renderer){
12180      /**
12181       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12182       */
12183     this.renderer = new Roo.UpdateManager.BasicRenderer();
12184     }
12185     
12186     Roo.UpdateManager.superclass.constructor.call(this);
12187 };
12188
12189 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12190     /**
12191      * Get the Element this UpdateManager is bound to
12192      * @return {Roo.Element} The element
12193      */
12194     getEl : function(){
12195         return this.el;
12196     },
12197     /**
12198      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12199      * @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:
12200 <pre><code>
12201 um.update({<br/>
12202     url: "your-url.php",<br/>
12203     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12204     callback: yourFunction,<br/>
12205     scope: yourObject, //(optional scope)  <br/>
12206     discardUrl: false, <br/>
12207     nocache: false,<br/>
12208     text: "Loading...",<br/>
12209     timeout: 30,<br/>
12210     scripts: false<br/>
12211 });
12212 </code></pre>
12213      * The only required property is url. The optional properties nocache, text and scripts
12214      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12215      * @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}
12216      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12217      * @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.
12218      */
12219     update : function(url, params, callback, discardUrl){
12220         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12221             var method = this.method,
12222                 cfg;
12223             if(typeof url == "object"){ // must be config object
12224                 cfg = url;
12225                 url = cfg.url;
12226                 params = params || cfg.params;
12227                 callback = callback || cfg.callback;
12228                 discardUrl = discardUrl || cfg.discardUrl;
12229                 if(callback && cfg.scope){
12230                     callback = callback.createDelegate(cfg.scope);
12231                 }
12232                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12233                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12234                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12235                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12236                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12237             }
12238             this.showLoading();
12239             if(!discardUrl){
12240                 this.defaultUrl = url;
12241             }
12242             if(typeof url == "function"){
12243                 url = url.call(this);
12244             }
12245
12246             method = method || (params ? "POST" : "GET");
12247             if(method == "GET"){
12248                 url = this.prepareUrl(url);
12249             }
12250
12251             var o = Roo.apply(cfg ||{}, {
12252                 url : url,
12253                 params: params,
12254                 success: this.successDelegate,
12255                 failure: this.failureDelegate,
12256                 callback: undefined,
12257                 timeout: (this.timeout*1000),
12258                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12259             });
12260             Roo.log("updated manager called with timeout of " + o.timeout);
12261             this.transaction = Roo.Ajax.request(o);
12262         }
12263     },
12264
12265     /**
12266      * 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.
12267      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12268      * @param {String/HTMLElement} form The form Id or form element
12269      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12270      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12271      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12272      */
12273     formUpdate : function(form, url, reset, callback){
12274         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12275             if(typeof url == "function"){
12276                 url = url.call(this);
12277             }
12278             form = Roo.getDom(form);
12279             this.transaction = Roo.Ajax.request({
12280                 form: form,
12281                 url:url,
12282                 success: this.successDelegate,
12283                 failure: this.failureDelegate,
12284                 timeout: (this.timeout*1000),
12285                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12286             });
12287             this.showLoading.defer(1, this);
12288         }
12289     },
12290
12291     /**
12292      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12293      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12294      */
12295     refresh : function(callback){
12296         if(this.defaultUrl == null){
12297             return;
12298         }
12299         this.update(this.defaultUrl, null, callback, true);
12300     },
12301
12302     /**
12303      * Set this element to auto refresh.
12304      * @param {Number} interval How often to update (in seconds).
12305      * @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)
12306      * @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}
12307      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12308      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12309      */
12310     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12311         if(refreshNow){
12312             this.update(url || this.defaultUrl, params, callback, true);
12313         }
12314         if(this.autoRefreshProcId){
12315             clearInterval(this.autoRefreshProcId);
12316         }
12317         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12318     },
12319
12320     /**
12321      * Stop auto refresh on this element.
12322      */
12323      stopAutoRefresh : function(){
12324         if(this.autoRefreshProcId){
12325             clearInterval(this.autoRefreshProcId);
12326             delete this.autoRefreshProcId;
12327         }
12328     },
12329
12330     isAutoRefreshing : function(){
12331        return this.autoRefreshProcId ? true : false;
12332     },
12333     /**
12334      * Called to update the element to "Loading" state. Override to perform custom action.
12335      */
12336     showLoading : function(){
12337         if(this.showLoadIndicator){
12338             this.el.update(this.indicatorText);
12339         }
12340     },
12341
12342     /**
12343      * Adds unique parameter to query string if disableCaching = true
12344      * @private
12345      */
12346     prepareUrl : function(url){
12347         if(this.disableCaching){
12348             var append = "_dc=" + (new Date().getTime());
12349             if(url.indexOf("?") !== -1){
12350                 url += "&" + append;
12351             }else{
12352                 url += "?" + append;
12353             }
12354         }
12355         return url;
12356     },
12357
12358     /**
12359      * @private
12360      */
12361     processSuccess : function(response){
12362         this.transaction = null;
12363         if(response.argument.form && response.argument.reset){
12364             try{ // put in try/catch since some older FF releases had problems with this
12365                 response.argument.form.reset();
12366             }catch(e){}
12367         }
12368         if(this.loadScripts){
12369             this.renderer.render(this.el, response, this,
12370                 this.updateComplete.createDelegate(this, [response]));
12371         }else{
12372             this.renderer.render(this.el, response, this);
12373             this.updateComplete(response);
12374         }
12375     },
12376
12377     updateComplete : function(response){
12378         this.fireEvent("update", this.el, response);
12379         if(typeof response.argument.callback == "function"){
12380             response.argument.callback(this.el, true, response);
12381         }
12382     },
12383
12384     /**
12385      * @private
12386      */
12387     processFailure : function(response){
12388         this.transaction = null;
12389         this.fireEvent("failure", this.el, response);
12390         if(typeof response.argument.callback == "function"){
12391             response.argument.callback(this.el, false, response);
12392         }
12393     },
12394
12395     /**
12396      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12397      * @param {Object} renderer The object implementing the render() method
12398      */
12399     setRenderer : function(renderer){
12400         this.renderer = renderer;
12401     },
12402
12403     getRenderer : function(){
12404        return this.renderer;
12405     },
12406
12407     /**
12408      * Set the defaultUrl used for updates
12409      * @param {String/Function} defaultUrl The url or a function to call to get the url
12410      */
12411     setDefaultUrl : function(defaultUrl){
12412         this.defaultUrl = defaultUrl;
12413     },
12414
12415     /**
12416      * Aborts the executing transaction
12417      */
12418     abort : function(){
12419         if(this.transaction){
12420             Roo.Ajax.abort(this.transaction);
12421         }
12422     },
12423
12424     /**
12425      * Returns true if an update is in progress
12426      * @return {Boolean}
12427      */
12428     isUpdating : function(){
12429         if(this.transaction){
12430             return Roo.Ajax.isLoading(this.transaction);
12431         }
12432         return false;
12433     }
12434 });
12435
12436 /**
12437  * @class Roo.UpdateManager.defaults
12438  * @static (not really - but it helps the doc tool)
12439  * The defaults collection enables customizing the default properties of UpdateManager
12440  */
12441    Roo.UpdateManager.defaults = {
12442        /**
12443          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12444          * @type Number
12445          */
12446          timeout : 30,
12447
12448          /**
12449          * True to process scripts by default (Defaults to false).
12450          * @type Boolean
12451          */
12452         loadScripts : false,
12453
12454         /**
12455         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12456         * @type String
12457         */
12458         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12459         /**
12460          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12461          * @type Boolean
12462          */
12463         disableCaching : false,
12464         /**
12465          * Whether to show indicatorText when loading (Defaults to true).
12466          * @type Boolean
12467          */
12468         showLoadIndicator : true,
12469         /**
12470          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12471          * @type String
12472          */
12473         indicatorText : '<div class="loading-indicator">Loading...</div>'
12474    };
12475
12476 /**
12477  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12478  *Usage:
12479  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12480  * @param {String/HTMLElement/Roo.Element} el The element to update
12481  * @param {String} url The url
12482  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12483  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12484  * @static
12485  * @deprecated
12486  * @member Roo.UpdateManager
12487  */
12488 Roo.UpdateManager.updateElement = function(el, url, params, options){
12489     var um = Roo.get(el, true).getUpdateManager();
12490     Roo.apply(um, options);
12491     um.update(url, params, options ? options.callback : null);
12492 };
12493 // alias for backwards compat
12494 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12495 /**
12496  * @class Roo.UpdateManager.BasicRenderer
12497  * Default Content renderer. Updates the elements innerHTML with the responseText.
12498  */
12499 Roo.UpdateManager.BasicRenderer = function(){};
12500
12501 Roo.UpdateManager.BasicRenderer.prototype = {
12502     /**
12503      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12504      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12505      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12506      * @param {Roo.Element} el The element being rendered
12507      * @param {Object} response The YUI Connect response object
12508      * @param {UpdateManager} updateManager The calling update manager
12509      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12510      */
12511      render : function(el, response, updateManager, callback){
12512         el.update(response.responseText, updateManager.loadScripts, callback);
12513     }
12514 };
12515 /*
12516  * Based on:
12517  * Roo JS
12518  * (c)) Alan Knowles
12519  * Licence : LGPL
12520  */
12521
12522
12523 /**
12524  * @class Roo.DomTemplate
12525  * @extends Roo.Template
12526  * An effort at a dom based template engine..
12527  *
12528  * Similar to XTemplate, except it uses dom parsing to create the template..
12529  *
12530  * Supported features:
12531  *
12532  *  Tags:
12533
12534 <pre><code>
12535       {a_variable} - output encoded.
12536       {a_variable.format:("Y-m-d")} - call a method on the variable
12537       {a_variable:raw} - unencoded output
12538       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12539       {a_variable:this.method_on_template(...)} - call a method on the template object.
12540  
12541 </code></pre>
12542  *  The tpl tag:
12543 <pre><code>
12544         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12545         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12546         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12547         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12548   
12549 </code></pre>
12550  *      
12551  */
12552 Roo.DomTemplate = function()
12553 {
12554      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12555      if (this.html) {
12556         this.compile();
12557      }
12558 };
12559
12560
12561 Roo.extend(Roo.DomTemplate, Roo.Template, {
12562     /**
12563      * id counter for sub templates.
12564      */
12565     id : 0,
12566     /**
12567      * flag to indicate if dom parser is inside a pre,
12568      * it will strip whitespace if not.
12569      */
12570     inPre : false,
12571     
12572     /**
12573      * The various sub templates
12574      */
12575     tpls : false,
12576     
12577     
12578     
12579     /**
12580      *
12581      * basic tag replacing syntax
12582      * WORD:WORD()
12583      *
12584      * // you can fake an object call by doing this
12585      *  x.t:(test,tesT) 
12586      * 
12587      */
12588     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12589     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12590     
12591     iterChild : function (node, method) {
12592         
12593         var oldPre = this.inPre;
12594         if (node.tagName == 'PRE') {
12595             this.inPre = true;
12596         }
12597         for( var i = 0; i < node.childNodes.length; i++) {
12598             method.call(this, node.childNodes[i]);
12599         }
12600         this.inPre = oldPre;
12601     },
12602     
12603     
12604     
12605     /**
12606      * compile the template
12607      *
12608      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12609      *
12610      */
12611     compile: function()
12612     {
12613         var s = this.html;
12614         
12615         // covert the html into DOM...
12616         var doc = false;
12617         var div =false;
12618         try {
12619             doc = document.implementation.createHTMLDocument("");
12620             doc.documentElement.innerHTML =   this.html  ;
12621             div = doc.documentElement;
12622         } catch (e) {
12623             // old IE... - nasty -- it causes all sorts of issues.. with
12624             // images getting pulled from server..
12625             div = document.createElement('div');
12626             div.innerHTML = this.html;
12627         }
12628         //doc.documentElement.innerHTML = htmlBody
12629          
12630         
12631         
12632         this.tpls = [];
12633         var _t = this;
12634         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12635         
12636         var tpls = this.tpls;
12637         
12638         // create a top level template from the snippet..
12639         
12640         //Roo.log(div.innerHTML);
12641         
12642         var tpl = {
12643             uid : 'master',
12644             id : this.id++,
12645             attr : false,
12646             value : false,
12647             body : div.innerHTML,
12648             
12649             forCall : false,
12650             execCall : false,
12651             dom : div,
12652             isTop : true
12653             
12654         };
12655         tpls.unshift(tpl);
12656         
12657         
12658         // compile them...
12659         this.tpls = [];
12660         Roo.each(tpls, function(tp){
12661             this.compileTpl(tp);
12662             this.tpls[tp.id] = tp;
12663         }, this);
12664         
12665         this.master = tpls[0];
12666         return this;
12667         
12668         
12669     },
12670     
12671     compileNode : function(node, istop) {
12672         // test for
12673         //Roo.log(node);
12674         
12675         
12676         // skip anything not a tag..
12677         if (node.nodeType != 1) {
12678             if (node.nodeType == 3 && !this.inPre) {
12679                 // reduce white space..
12680                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12681                 
12682             }
12683             return;
12684         }
12685         
12686         var tpl = {
12687             uid : false,
12688             id : false,
12689             attr : false,
12690             value : false,
12691             body : '',
12692             
12693             forCall : false,
12694             execCall : false,
12695             dom : false,
12696             isTop : istop
12697             
12698             
12699         };
12700         
12701         
12702         switch(true) {
12703             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12704             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12705             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12706             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12707             // no default..
12708         }
12709         
12710         
12711         if (!tpl.attr) {
12712             // just itterate children..
12713             this.iterChild(node,this.compileNode);
12714             return;
12715         }
12716         tpl.uid = this.id++;
12717         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12718         node.removeAttribute('roo-'+ tpl.attr);
12719         if (tpl.attr != 'name') {
12720             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12721             node.parentNode.replaceChild(placeholder,  node);
12722         } else {
12723             
12724             var placeholder =  document.createElement('span');
12725             placeholder.className = 'roo-tpl-' + tpl.value;
12726             node.parentNode.replaceChild(placeholder,  node);
12727         }
12728         
12729         // parent now sees '{domtplXXXX}
12730         this.iterChild(node,this.compileNode);
12731         
12732         // we should now have node body...
12733         var div = document.createElement('div');
12734         div.appendChild(node);
12735         tpl.dom = node;
12736         // this has the unfortunate side effect of converting tagged attributes
12737         // eg. href="{...}" into %7C...%7D
12738         // this has been fixed by searching for those combo's although it's a bit hacky..
12739         
12740         
12741         tpl.body = div.innerHTML;
12742         
12743         
12744          
12745         tpl.id = tpl.uid;
12746         switch(tpl.attr) {
12747             case 'for' :
12748                 switch (tpl.value) {
12749                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12750                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12751                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12752                 }
12753                 break;
12754             
12755             case 'exec':
12756                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12757                 break;
12758             
12759             case 'if':     
12760                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12761                 break;
12762             
12763             case 'name':
12764                 tpl.id  = tpl.value; // replace non characters???
12765                 break;
12766             
12767         }
12768         
12769         
12770         this.tpls.push(tpl);
12771         
12772         
12773         
12774     },
12775     
12776     
12777     
12778     
12779     /**
12780      * Compile a segment of the template into a 'sub-template'
12781      *
12782      * 
12783      * 
12784      *
12785      */
12786     compileTpl : function(tpl)
12787     {
12788         var fm = Roo.util.Format;
12789         var useF = this.disableFormats !== true;
12790         
12791         var sep = Roo.isGecko ? "+\n" : ",\n";
12792         
12793         var undef = function(str) {
12794             Roo.debug && Roo.log("Property not found :"  + str);
12795             return '';
12796         };
12797           
12798         //Roo.log(tpl.body);
12799         
12800         
12801         
12802         var fn = function(m, lbrace, name, format, args)
12803         {
12804             //Roo.log("ARGS");
12805             //Roo.log(arguments);
12806             args = args ? args.replace(/\\'/g,"'") : args;
12807             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12808             if (typeof(format) == 'undefined') {
12809                 format =  'htmlEncode'; 
12810             }
12811             if (format == 'raw' ) {
12812                 format = false;
12813             }
12814             
12815             if(name.substr(0, 6) == 'domtpl'){
12816                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12817             }
12818             
12819             // build an array of options to determine if value is undefined..
12820             
12821             // basically get 'xxxx.yyyy' then do
12822             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12823             //    (function () { Roo.log("Property not found"); return ''; })() :
12824             //    ......
12825             
12826             var udef_ar = [];
12827             var lookfor = '';
12828             Roo.each(name.split('.'), function(st) {
12829                 lookfor += (lookfor.length ? '.': '') + st;
12830                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12831             });
12832             
12833             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12834             
12835             
12836             if(format && useF){
12837                 
12838                 args = args ? ',' + args : "";
12839                  
12840                 if(format.substr(0, 5) != "this."){
12841                     format = "fm." + format + '(';
12842                 }else{
12843                     format = 'this.call("'+ format.substr(5) + '", ';
12844                     args = ", values";
12845                 }
12846                 
12847                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12848             }
12849              
12850             if (args && args.length) {
12851                 // called with xxyx.yuu:(test,test)
12852                 // change to ()
12853                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12854             }
12855             // raw.. - :raw modifier..
12856             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12857             
12858         };
12859         var body;
12860         // branched to use + in gecko and [].join() in others
12861         if(Roo.isGecko){
12862             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12863                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12864                     "';};};";
12865         }else{
12866             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12867             body.push(tpl.body.replace(/(\r\n|\n)/g,
12868                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12869             body.push("'].join('');};};");
12870             body = body.join('');
12871         }
12872         
12873         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12874        
12875         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12876         eval(body);
12877         
12878         return this;
12879     },
12880      
12881     /**
12882      * same as applyTemplate, except it's done to one of the subTemplates
12883      * when using named templates, you can do:
12884      *
12885      * var str = pl.applySubTemplate('your-name', values);
12886      *
12887      * 
12888      * @param {Number} id of the template
12889      * @param {Object} values to apply to template
12890      * @param {Object} parent (normaly the instance of this object)
12891      */
12892     applySubTemplate : function(id, values, parent)
12893     {
12894         
12895         
12896         var t = this.tpls[id];
12897         
12898         
12899         try { 
12900             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12901                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12902                 return '';
12903             }
12904         } catch(e) {
12905             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12906             Roo.log(values);
12907           
12908             return '';
12909         }
12910         try { 
12911             
12912             if(t.execCall && t.execCall.call(this, values, parent)){
12913                 return '';
12914             }
12915         } catch(e) {
12916             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12917             Roo.log(values);
12918             return '';
12919         }
12920         
12921         try {
12922             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12923             parent = t.target ? values : parent;
12924             if(t.forCall && vs instanceof Array){
12925                 var buf = [];
12926                 for(var i = 0, len = vs.length; i < len; i++){
12927                     try {
12928                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12929                     } catch (e) {
12930                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12931                         Roo.log(e.body);
12932                         //Roo.log(t.compiled);
12933                         Roo.log(vs[i]);
12934                     }   
12935                 }
12936                 return buf.join('');
12937             }
12938         } catch (e) {
12939             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12940             Roo.log(values);
12941             return '';
12942         }
12943         try {
12944             return t.compiled.call(this, vs, parent);
12945         } catch (e) {
12946             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12947             Roo.log(e.body);
12948             //Roo.log(t.compiled);
12949             Roo.log(values);
12950             return '';
12951         }
12952     },
12953
12954    
12955
12956     applyTemplate : function(values){
12957         return this.master.compiled.call(this, values, {});
12958         //var s = this.subs;
12959     },
12960
12961     apply : function(){
12962         return this.applyTemplate.apply(this, arguments);
12963     }
12964
12965  });
12966
12967 Roo.DomTemplate.from = function(el){
12968     el = Roo.getDom(el);
12969     return new Roo.Domtemplate(el.value || el.innerHTML);
12970 };/*
12971  * Based on:
12972  * Ext JS Library 1.1.1
12973  * Copyright(c) 2006-2007, Ext JS, LLC.
12974  *
12975  * Originally Released Under LGPL - original licence link has changed is not relivant.
12976  *
12977  * Fork - LGPL
12978  * <script type="text/javascript">
12979  */
12980
12981 /**
12982  * @class Roo.util.DelayedTask
12983  * Provides a convenient method of performing setTimeout where a new
12984  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12985  * You can use this class to buffer
12986  * the keypress events for a certain number of milliseconds, and perform only if they stop
12987  * for that amount of time.
12988  * @constructor The parameters to this constructor serve as defaults and are not required.
12989  * @param {Function} fn (optional) The default function to timeout
12990  * @param {Object} scope (optional) The default scope of that timeout
12991  * @param {Array} args (optional) The default Array of arguments
12992  */
12993 Roo.util.DelayedTask = function(fn, scope, args){
12994     var id = null, d, t;
12995
12996     var call = function(){
12997         var now = new Date().getTime();
12998         if(now - t >= d){
12999             clearInterval(id);
13000             id = null;
13001             fn.apply(scope, args || []);
13002         }
13003     };
13004     /**
13005      * Cancels any pending timeout and queues a new one
13006      * @param {Number} delay The milliseconds to delay
13007      * @param {Function} newFn (optional) Overrides function passed to constructor
13008      * @param {Object} newScope (optional) Overrides scope passed to constructor
13009      * @param {Array} newArgs (optional) Overrides args passed to constructor
13010      */
13011     this.delay = function(delay, newFn, newScope, newArgs){
13012         if(id && delay != d){
13013             this.cancel();
13014         }
13015         d = delay;
13016         t = new Date().getTime();
13017         fn = newFn || fn;
13018         scope = newScope || scope;
13019         args = newArgs || args;
13020         if(!id){
13021             id = setInterval(call, d);
13022         }
13023     };
13024
13025     /**
13026      * Cancel the last queued timeout
13027      */
13028     this.cancel = function(){
13029         if(id){
13030             clearInterval(id);
13031             id = null;
13032         }
13033     };
13034 };/*
13035  * Based on:
13036  * Ext JS Library 1.1.1
13037  * Copyright(c) 2006-2007, Ext JS, LLC.
13038  *
13039  * Originally Released Under LGPL - original licence link has changed is not relivant.
13040  *
13041  * Fork - LGPL
13042  * <script type="text/javascript">
13043  */
13044  
13045  
13046 Roo.util.TaskRunner = function(interval){
13047     interval = interval || 10;
13048     var tasks = [], removeQueue = [];
13049     var id = 0;
13050     var running = false;
13051
13052     var stopThread = function(){
13053         running = false;
13054         clearInterval(id);
13055         id = 0;
13056     };
13057
13058     var startThread = function(){
13059         if(!running){
13060             running = true;
13061             id = setInterval(runTasks, interval);
13062         }
13063     };
13064
13065     var removeTask = function(task){
13066         removeQueue.push(task);
13067         if(task.onStop){
13068             task.onStop();
13069         }
13070     };
13071
13072     var runTasks = function(){
13073         if(removeQueue.length > 0){
13074             for(var i = 0, len = removeQueue.length; i < len; i++){
13075                 tasks.remove(removeQueue[i]);
13076             }
13077             removeQueue = [];
13078             if(tasks.length < 1){
13079                 stopThread();
13080                 return;
13081             }
13082         }
13083         var now = new Date().getTime();
13084         for(var i = 0, len = tasks.length; i < len; ++i){
13085             var t = tasks[i];
13086             var itime = now - t.taskRunTime;
13087             if(t.interval <= itime){
13088                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13089                 t.taskRunTime = now;
13090                 if(rt === false || t.taskRunCount === t.repeat){
13091                     removeTask(t);
13092                     return;
13093                 }
13094             }
13095             if(t.duration && t.duration <= (now - t.taskStartTime)){
13096                 removeTask(t);
13097             }
13098         }
13099     };
13100
13101     /**
13102      * Queues a new task.
13103      * @param {Object} task
13104      */
13105     this.start = function(task){
13106         tasks.push(task);
13107         task.taskStartTime = new Date().getTime();
13108         task.taskRunTime = 0;
13109         task.taskRunCount = 0;
13110         startThread();
13111         return task;
13112     };
13113
13114     this.stop = function(task){
13115         removeTask(task);
13116         return task;
13117     };
13118
13119     this.stopAll = function(){
13120         stopThread();
13121         for(var i = 0, len = tasks.length; i < len; i++){
13122             if(tasks[i].onStop){
13123                 tasks[i].onStop();
13124             }
13125         }
13126         tasks = [];
13127         removeQueue = [];
13128     };
13129 };
13130
13131 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13132  * Based on:
13133  * Ext JS Library 1.1.1
13134  * Copyright(c) 2006-2007, Ext JS, LLC.
13135  *
13136  * Originally Released Under LGPL - original licence link has changed is not relivant.
13137  *
13138  * Fork - LGPL
13139  * <script type="text/javascript">
13140  */
13141
13142  
13143 /**
13144  * @class Roo.util.MixedCollection
13145  * @extends Roo.util.Observable
13146  * A Collection class that maintains both numeric indexes and keys and exposes events.
13147  * @constructor
13148  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13149  * collection (defaults to false)
13150  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13151  * and return the key value for that item.  This is used when available to look up the key on items that
13152  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13153  * equivalent to providing an implementation for the {@link #getKey} method.
13154  */
13155 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13156     this.items = [];
13157     this.map = {};
13158     this.keys = [];
13159     this.length = 0;
13160     this.addEvents({
13161         /**
13162          * @event clear
13163          * Fires when the collection is cleared.
13164          */
13165         "clear" : true,
13166         /**
13167          * @event add
13168          * Fires when an item is added to the collection.
13169          * @param {Number} index The index at which the item was added.
13170          * @param {Object} o The item added.
13171          * @param {String} key The key associated with the added item.
13172          */
13173         "add" : true,
13174         /**
13175          * @event replace
13176          * Fires when an item is replaced in the collection.
13177          * @param {String} key he key associated with the new added.
13178          * @param {Object} old The item being replaced.
13179          * @param {Object} new The new item.
13180          */
13181         "replace" : true,
13182         /**
13183          * @event remove
13184          * Fires when an item is removed from the collection.
13185          * @param {Object} o The item being removed.
13186          * @param {String} key (optional) The key associated with the removed item.
13187          */
13188         "remove" : true,
13189         "sort" : true
13190     });
13191     this.allowFunctions = allowFunctions === true;
13192     if(keyFn){
13193         this.getKey = keyFn;
13194     }
13195     Roo.util.MixedCollection.superclass.constructor.call(this);
13196 };
13197
13198 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13199     allowFunctions : false,
13200     
13201 /**
13202  * Adds an item to the collection.
13203  * @param {String} key The key to associate with the item
13204  * @param {Object} o The item to add.
13205  * @return {Object} The item added.
13206  */
13207     add : function(key, o){
13208         if(arguments.length == 1){
13209             o = arguments[0];
13210             key = this.getKey(o);
13211         }
13212         if(typeof key == "undefined" || key === null){
13213             this.length++;
13214             this.items.push(o);
13215             this.keys.push(null);
13216         }else{
13217             var old = this.map[key];
13218             if(old){
13219                 return this.replace(key, o);
13220             }
13221             this.length++;
13222             this.items.push(o);
13223             this.map[key] = o;
13224             this.keys.push(key);
13225         }
13226         this.fireEvent("add", this.length-1, o, key);
13227         return o;
13228     },
13229        
13230 /**
13231   * MixedCollection has a generic way to fetch keys if you implement getKey.
13232 <pre><code>
13233 // normal way
13234 var mc = new Roo.util.MixedCollection();
13235 mc.add(someEl.dom.id, someEl);
13236 mc.add(otherEl.dom.id, otherEl);
13237 //and so on
13238
13239 // using getKey
13240 var mc = new Roo.util.MixedCollection();
13241 mc.getKey = function(el){
13242    return el.dom.id;
13243 };
13244 mc.add(someEl);
13245 mc.add(otherEl);
13246
13247 // or via the constructor
13248 var mc = new Roo.util.MixedCollection(false, function(el){
13249    return el.dom.id;
13250 });
13251 mc.add(someEl);
13252 mc.add(otherEl);
13253 </code></pre>
13254  * @param o {Object} The item for which to find the key.
13255  * @return {Object} The key for the passed item.
13256  */
13257     getKey : function(o){
13258          return o.id; 
13259     },
13260    
13261 /**
13262  * Replaces an item in the collection.
13263  * @param {String} key The key associated with the item to replace, or the item to replace.
13264  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13265  * @return {Object}  The new item.
13266  */
13267     replace : function(key, o){
13268         if(arguments.length == 1){
13269             o = arguments[0];
13270             key = this.getKey(o);
13271         }
13272         var old = this.item(key);
13273         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13274              return this.add(key, o);
13275         }
13276         var index = this.indexOfKey(key);
13277         this.items[index] = o;
13278         this.map[key] = o;
13279         this.fireEvent("replace", key, old, o);
13280         return o;
13281     },
13282    
13283 /**
13284  * Adds all elements of an Array or an Object to the collection.
13285  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13286  * an Array of values, each of which are added to the collection.
13287  */
13288     addAll : function(objs){
13289         if(arguments.length > 1 || objs instanceof Array){
13290             var args = arguments.length > 1 ? arguments : objs;
13291             for(var i = 0, len = args.length; i < len; i++){
13292                 this.add(args[i]);
13293             }
13294         }else{
13295             for(var key in objs){
13296                 if(this.allowFunctions || typeof objs[key] != "function"){
13297                     this.add(key, objs[key]);
13298                 }
13299             }
13300         }
13301     },
13302    
13303 /**
13304  * Executes the specified function once for every item in the collection, passing each
13305  * item as the first and only parameter. returning false from the function will stop the iteration.
13306  * @param {Function} fn The function to execute for each item.
13307  * @param {Object} scope (optional) The scope in which to execute the function.
13308  */
13309     each : function(fn, scope){
13310         var items = [].concat(this.items); // each safe for removal
13311         for(var i = 0, len = items.length; i < len; i++){
13312             if(fn.call(scope || items[i], items[i], i, len) === false){
13313                 break;
13314             }
13315         }
13316     },
13317    
13318 /**
13319  * Executes the specified function once for every key in the collection, passing each
13320  * key, and its associated item as the first two parameters.
13321  * @param {Function} fn The function to execute for each item.
13322  * @param {Object} scope (optional) The scope in which to execute the function.
13323  */
13324     eachKey : function(fn, scope){
13325         for(var i = 0, len = this.keys.length; i < len; i++){
13326             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13327         }
13328     },
13329    
13330 /**
13331  * Returns the first item in the collection which elicits a true return value from the
13332  * passed selection function.
13333  * @param {Function} fn The selection function to execute for each item.
13334  * @param {Object} scope (optional) The scope in which to execute the function.
13335  * @return {Object} The first item in the collection which returned true from the selection function.
13336  */
13337     find : function(fn, scope){
13338         for(var i = 0, len = this.items.length; i < len; i++){
13339             if(fn.call(scope || window, this.items[i], this.keys[i])){
13340                 return this.items[i];
13341             }
13342         }
13343         return null;
13344     },
13345    
13346 /**
13347  * Inserts an item at the specified index in the collection.
13348  * @param {Number} index The index to insert the item at.
13349  * @param {String} key The key to associate with the new item, or the item itself.
13350  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13351  * @return {Object} The item inserted.
13352  */
13353     insert : function(index, key, o){
13354         if(arguments.length == 2){
13355             o = arguments[1];
13356             key = this.getKey(o);
13357         }
13358         if(index >= this.length){
13359             return this.add(key, o);
13360         }
13361         this.length++;
13362         this.items.splice(index, 0, o);
13363         if(typeof key != "undefined" && key != null){
13364             this.map[key] = o;
13365         }
13366         this.keys.splice(index, 0, key);
13367         this.fireEvent("add", index, o, key);
13368         return o;
13369     },
13370    
13371 /**
13372  * Removed an item from the collection.
13373  * @param {Object} o The item to remove.
13374  * @return {Object} The item removed.
13375  */
13376     remove : function(o){
13377         return this.removeAt(this.indexOf(o));
13378     },
13379    
13380 /**
13381  * Remove an item from a specified index in the collection.
13382  * @param {Number} index The index within the collection of the item to remove.
13383  */
13384     removeAt : function(index){
13385         if(index < this.length && index >= 0){
13386             this.length--;
13387             var o = this.items[index];
13388             this.items.splice(index, 1);
13389             var key = this.keys[index];
13390             if(typeof key != "undefined"){
13391                 delete this.map[key];
13392             }
13393             this.keys.splice(index, 1);
13394             this.fireEvent("remove", o, key);
13395         }
13396     },
13397    
13398 /**
13399  * Removed an item associated with the passed key fom the collection.
13400  * @param {String} key The key of the item to remove.
13401  */
13402     removeKey : function(key){
13403         return this.removeAt(this.indexOfKey(key));
13404     },
13405    
13406 /**
13407  * Returns the number of items in the collection.
13408  * @return {Number} the number of items in the collection.
13409  */
13410     getCount : function(){
13411         return this.length; 
13412     },
13413    
13414 /**
13415  * Returns index within the collection of the passed Object.
13416  * @param {Object} o The item to find the index of.
13417  * @return {Number} index of the item.
13418  */
13419     indexOf : function(o){
13420         if(!this.items.indexOf){
13421             for(var i = 0, len = this.items.length; i < len; i++){
13422                 if(this.items[i] == o) {
13423                     return i;
13424                 }
13425             }
13426             return -1;
13427         }else{
13428             return this.items.indexOf(o);
13429         }
13430     },
13431    
13432 /**
13433  * Returns index within the collection of the passed key.
13434  * @param {String} key The key to find the index of.
13435  * @return {Number} index of the key.
13436  */
13437     indexOfKey : function(key){
13438         if(!this.keys.indexOf){
13439             for(var i = 0, len = this.keys.length; i < len; i++){
13440                 if(this.keys[i] == key) {
13441                     return i;
13442                 }
13443             }
13444             return -1;
13445         }else{
13446             return this.keys.indexOf(key);
13447         }
13448     },
13449    
13450 /**
13451  * Returns the item associated with the passed key OR index. Key has priority over index.
13452  * @param {String/Number} key The key or index of the item.
13453  * @return {Object} The item associated with the passed key.
13454  */
13455     item : function(key){
13456         if (key === 'length') {
13457             return null;
13458         }
13459         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13460         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13461     },
13462     
13463 /**
13464  * Returns the item at the specified index.
13465  * @param {Number} index The index of the item.
13466  * @return {Object}
13467  */
13468     itemAt : function(index){
13469         return this.items[index];
13470     },
13471     
13472 /**
13473  * Returns the item associated with the passed key.
13474  * @param {String/Number} key The key of the item.
13475  * @return {Object} The item associated with the passed key.
13476  */
13477     key : function(key){
13478         return this.map[key];
13479     },
13480    
13481 /**
13482  * Returns true if the collection contains the passed Object as an item.
13483  * @param {Object} o  The Object to look for in the collection.
13484  * @return {Boolean} True if the collection contains the Object as an item.
13485  */
13486     contains : function(o){
13487         return this.indexOf(o) != -1;
13488     },
13489    
13490 /**
13491  * Returns true if the collection contains the passed Object as a key.
13492  * @param {String} key The key to look for in the collection.
13493  * @return {Boolean} True if the collection contains the Object as a key.
13494  */
13495     containsKey : function(key){
13496         return typeof this.map[key] != "undefined";
13497     },
13498    
13499 /**
13500  * Removes all items from the collection.
13501  */
13502     clear : function(){
13503         this.length = 0;
13504         this.items = [];
13505         this.keys = [];
13506         this.map = {};
13507         this.fireEvent("clear");
13508     },
13509    
13510 /**
13511  * Returns the first item in the collection.
13512  * @return {Object} the first item in the collection..
13513  */
13514     first : function(){
13515         return this.items[0]; 
13516     },
13517    
13518 /**
13519  * Returns the last item in the collection.
13520  * @return {Object} the last item in the collection..
13521  */
13522     last : function(){
13523         return this.items[this.length-1];   
13524     },
13525     
13526     _sort : function(property, dir, fn){
13527         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13528         fn = fn || function(a, b){
13529             return a-b;
13530         };
13531         var c = [], k = this.keys, items = this.items;
13532         for(var i = 0, len = items.length; i < len; i++){
13533             c[c.length] = {key: k[i], value: items[i], index: i};
13534         }
13535         c.sort(function(a, b){
13536             var v = fn(a[property], b[property]) * dsc;
13537             if(v == 0){
13538                 v = (a.index < b.index ? -1 : 1);
13539             }
13540             return v;
13541         });
13542         for(var i = 0, len = c.length; i < len; i++){
13543             items[i] = c[i].value;
13544             k[i] = c[i].key;
13545         }
13546         this.fireEvent("sort", this);
13547     },
13548     
13549     /**
13550      * Sorts this collection with the passed comparison function
13551      * @param {String} direction (optional) "ASC" or "DESC"
13552      * @param {Function} fn (optional) comparison function
13553      */
13554     sort : function(dir, fn){
13555         this._sort("value", dir, fn);
13556     },
13557     
13558     /**
13559      * Sorts this collection by keys
13560      * @param {String} direction (optional) "ASC" or "DESC"
13561      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13562      */
13563     keySort : function(dir, fn){
13564         this._sort("key", dir, fn || function(a, b){
13565             return String(a).toUpperCase()-String(b).toUpperCase();
13566         });
13567     },
13568     
13569     /**
13570      * Returns a range of items in this collection
13571      * @param {Number} startIndex (optional) defaults to 0
13572      * @param {Number} endIndex (optional) default to the last item
13573      * @return {Array} An array of items
13574      */
13575     getRange : function(start, end){
13576         var items = this.items;
13577         if(items.length < 1){
13578             return [];
13579         }
13580         start = start || 0;
13581         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13582         var r = [];
13583         if(start <= end){
13584             for(var i = start; i <= end; i++) {
13585                     r[r.length] = items[i];
13586             }
13587         }else{
13588             for(var i = start; i >= end; i--) {
13589                     r[r.length] = items[i];
13590             }
13591         }
13592         return r;
13593     },
13594         
13595     /**
13596      * Filter the <i>objects</i> in this collection by a specific property. 
13597      * Returns a new collection that has been filtered.
13598      * @param {String} property A property on your objects
13599      * @param {String/RegExp} value Either string that the property values 
13600      * should start with or a RegExp to test against the property
13601      * @return {MixedCollection} The new filtered collection
13602      */
13603     filter : function(property, value){
13604         if(!value.exec){ // not a regex
13605             value = String(value);
13606             if(value.length == 0){
13607                 return this.clone();
13608             }
13609             value = new RegExp("^" + Roo.escapeRe(value), "i");
13610         }
13611         return this.filterBy(function(o){
13612             return o && value.test(o[property]);
13613         });
13614         },
13615     
13616     /**
13617      * Filter by a function. * Returns a new collection that has been filtered.
13618      * The passed function will be called with each 
13619      * object in the collection. If the function returns true, the value is included 
13620      * otherwise it is filtered.
13621      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13622      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13623      * @return {MixedCollection} The new filtered collection
13624      */
13625     filterBy : function(fn, scope){
13626         var r = new Roo.util.MixedCollection();
13627         r.getKey = this.getKey;
13628         var k = this.keys, it = this.items;
13629         for(var i = 0, len = it.length; i < len; i++){
13630             if(fn.call(scope||this, it[i], k[i])){
13631                                 r.add(k[i], it[i]);
13632                         }
13633         }
13634         return r;
13635     },
13636     
13637     /**
13638      * Creates a duplicate of this collection
13639      * @return {MixedCollection}
13640      */
13641     clone : function(){
13642         var r = new Roo.util.MixedCollection();
13643         var k = this.keys, it = this.items;
13644         for(var i = 0, len = it.length; i < len; i++){
13645             r.add(k[i], it[i]);
13646         }
13647         r.getKey = this.getKey;
13648         return r;
13649     }
13650 });
13651 /**
13652  * Returns the item associated with the passed key or index.
13653  * @method
13654  * @param {String/Number} key The key or index of the item.
13655  * @return {Object} The item associated with the passed key.
13656  */
13657 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13658  * Based on:
13659  * Ext JS Library 1.1.1
13660  * Copyright(c) 2006-2007, Ext JS, LLC.
13661  *
13662  * Originally Released Under LGPL - original licence link has changed is not relivant.
13663  *
13664  * Fork - LGPL
13665  * <script type="text/javascript">
13666  */
13667 /**
13668  * @class Roo.util.JSON
13669  * Modified version of Douglas Crockford"s json.js that doesn"t
13670  * mess with the Object prototype 
13671  * http://www.json.org/js.html
13672  * @singleton
13673  */
13674 Roo.util.JSON = new (function(){
13675     var useHasOwn = {}.hasOwnProperty ? true : false;
13676     
13677     // crashes Safari in some instances
13678     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13679     
13680     var pad = function(n) {
13681         return n < 10 ? "0" + n : n;
13682     };
13683     
13684     var m = {
13685         "\b": '\\b',
13686         "\t": '\\t',
13687         "\n": '\\n',
13688         "\f": '\\f',
13689         "\r": '\\r',
13690         '"' : '\\"',
13691         "\\": '\\\\'
13692     };
13693
13694     var encodeString = function(s){
13695         if (/["\\\x00-\x1f]/.test(s)) {
13696             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13697                 var c = m[b];
13698                 if(c){
13699                     return c;
13700                 }
13701                 c = b.charCodeAt();
13702                 return "\\u00" +
13703                     Math.floor(c / 16).toString(16) +
13704                     (c % 16).toString(16);
13705             }) + '"';
13706         }
13707         return '"' + s + '"';
13708     };
13709     
13710     var encodeArray = function(o){
13711         var a = ["["], b, i, l = o.length, v;
13712             for (i = 0; i < l; i += 1) {
13713                 v = o[i];
13714                 switch (typeof v) {
13715                     case "undefined":
13716                     case "function":
13717                     case "unknown":
13718                         break;
13719                     default:
13720                         if (b) {
13721                             a.push(',');
13722                         }
13723                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13724                         b = true;
13725                 }
13726             }
13727             a.push("]");
13728             return a.join("");
13729     };
13730     
13731     var encodeDate = function(o){
13732         return '"' + o.getFullYear() + "-" +
13733                 pad(o.getMonth() + 1) + "-" +
13734                 pad(o.getDate()) + "T" +
13735                 pad(o.getHours()) + ":" +
13736                 pad(o.getMinutes()) + ":" +
13737                 pad(o.getSeconds()) + '"';
13738     };
13739     
13740     /**
13741      * Encodes an Object, Array or other value
13742      * @param {Mixed} o The variable to encode
13743      * @return {String} The JSON string
13744      */
13745     this.encode = function(o)
13746     {
13747         // should this be extended to fully wrap stringify..
13748         
13749         if(typeof o == "undefined" || o === null){
13750             return "null";
13751         }else if(o instanceof Array){
13752             return encodeArray(o);
13753         }else if(o instanceof Date){
13754             return encodeDate(o);
13755         }else if(typeof o == "string"){
13756             return encodeString(o);
13757         }else if(typeof o == "number"){
13758             return isFinite(o) ? String(o) : "null";
13759         }else if(typeof o == "boolean"){
13760             return String(o);
13761         }else {
13762             var a = ["{"], b, i, v;
13763             for (i in o) {
13764                 if(!useHasOwn || o.hasOwnProperty(i)) {
13765                     v = o[i];
13766                     switch (typeof v) {
13767                     case "undefined":
13768                     case "function":
13769                     case "unknown":
13770                         break;
13771                     default:
13772                         if(b){
13773                             a.push(',');
13774                         }
13775                         a.push(this.encode(i), ":",
13776                                 v === null ? "null" : this.encode(v));
13777                         b = true;
13778                     }
13779                 }
13780             }
13781             a.push("}");
13782             return a.join("");
13783         }
13784     };
13785     
13786     /**
13787      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13788      * @param {String} json The JSON string
13789      * @return {Object} The resulting object
13790      */
13791     this.decode = function(json){
13792         
13793         return  /** eval:var:json */ eval("(" + json + ')');
13794     };
13795 })();
13796 /** 
13797  * Shorthand for {@link Roo.util.JSON#encode}
13798  * @member Roo encode 
13799  * @method */
13800 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13801 /** 
13802  * Shorthand for {@link Roo.util.JSON#decode}
13803  * @member Roo decode 
13804  * @method */
13805 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13806 /*
13807  * Based on:
13808  * Ext JS Library 1.1.1
13809  * Copyright(c) 2006-2007, Ext JS, LLC.
13810  *
13811  * Originally Released Under LGPL - original licence link has changed is not relivant.
13812  *
13813  * Fork - LGPL
13814  * <script type="text/javascript">
13815  */
13816  
13817 /**
13818  * @class Roo.util.Format
13819  * Reusable data formatting functions
13820  * @singleton
13821  */
13822 Roo.util.Format = function(){
13823     var trimRe = /^\s+|\s+$/g;
13824     return {
13825         /**
13826          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13827          * @param {String} value The string to truncate
13828          * @param {Number} length The maximum length to allow before truncating
13829          * @return {String} The converted text
13830          */
13831         ellipsis : function(value, len){
13832             if(value && value.length > len){
13833                 return value.substr(0, len-3)+"...";
13834             }
13835             return value;
13836         },
13837
13838         /**
13839          * Checks a reference and converts it to empty string if it is undefined
13840          * @param {Mixed} value Reference to check
13841          * @return {Mixed} Empty string if converted, otherwise the original value
13842          */
13843         undef : function(value){
13844             return typeof value != "undefined" ? value : "";
13845         },
13846
13847         /**
13848          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13849          * @param {String} value The string to encode
13850          * @return {String} The encoded text
13851          */
13852         htmlEncode : function(value){
13853             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13854         },
13855
13856         /**
13857          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13858          * @param {String} value The string to decode
13859          * @return {String} The decoded text
13860          */
13861         htmlDecode : function(value){
13862             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13863         },
13864
13865         /**
13866          * Trims any whitespace from either side of a string
13867          * @param {String} value The text to trim
13868          * @return {String} The trimmed text
13869          */
13870         trim : function(value){
13871             return String(value).replace(trimRe, "");
13872         },
13873
13874         /**
13875          * Returns a substring from within an original string
13876          * @param {String} value The original text
13877          * @param {Number} start The start index of the substring
13878          * @param {Number} length The length of the substring
13879          * @return {String} The substring
13880          */
13881         substr : function(value, start, length){
13882             return String(value).substr(start, length);
13883         },
13884
13885         /**
13886          * Converts a string to all lower case letters
13887          * @param {String} value The text to convert
13888          * @return {String} The converted text
13889          */
13890         lowercase : function(value){
13891             return String(value).toLowerCase();
13892         },
13893
13894         /**
13895          * Converts a string to all upper case letters
13896          * @param {String} value The text to convert
13897          * @return {String} The converted text
13898          */
13899         uppercase : function(value){
13900             return String(value).toUpperCase();
13901         },
13902
13903         /**
13904          * Converts the first character only of a string to upper case
13905          * @param {String} value The text to convert
13906          * @return {String} The converted text
13907          */
13908         capitalize : function(value){
13909             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13910         },
13911
13912         // private
13913         call : function(value, fn){
13914             if(arguments.length > 2){
13915                 var args = Array.prototype.slice.call(arguments, 2);
13916                 args.unshift(value);
13917                  
13918                 return /** eval:var:value */  eval(fn).apply(window, args);
13919             }else{
13920                 /** eval:var:value */
13921                 return /** eval:var:value */ eval(fn).call(window, value);
13922             }
13923         },
13924
13925        
13926         /**
13927          * safer version of Math.toFixed..??/
13928          * @param {Number/String} value The numeric value to format
13929          * @param {Number/String} value Decimal places 
13930          * @return {String} The formatted currency string
13931          */
13932         toFixed : function(v, n)
13933         {
13934             // why not use to fixed - precision is buggered???
13935             if (!n) {
13936                 return Math.round(v-0);
13937             }
13938             var fact = Math.pow(10,n+1);
13939             v = (Math.round((v-0)*fact))/fact;
13940             var z = (''+fact).substring(2);
13941             if (v == Math.floor(v)) {
13942                 return Math.floor(v) + '.' + z;
13943             }
13944             
13945             // now just padd decimals..
13946             var ps = String(v).split('.');
13947             var fd = (ps[1] + z);
13948             var r = fd.substring(0,n); 
13949             var rm = fd.substring(n); 
13950             if (rm < 5) {
13951                 return ps[0] + '.' + r;
13952             }
13953             r*=1; // turn it into a number;
13954             r++;
13955             if (String(r).length != n) {
13956                 ps[0]*=1;
13957                 ps[0]++;
13958                 r = String(r).substring(1); // chop the end off.
13959             }
13960             
13961             return ps[0] + '.' + r;
13962              
13963         },
13964         
13965         /**
13966          * Format a number as US currency
13967          * @param {Number/String} value The numeric value to format
13968          * @return {String} The formatted currency string
13969          */
13970         usMoney : function(v){
13971             return '$' + Roo.util.Format.number(v);
13972         },
13973         
13974         /**
13975          * Format a number
13976          * eventually this should probably emulate php's number_format
13977          * @param {Number/String} value The numeric value to format
13978          * @param {Number} decimals number of decimal places
13979          * @param {String} delimiter for thousands (default comma)
13980          * @return {String} The formatted currency string
13981          */
13982         number : function(v, decimals, thousandsDelimiter)
13983         {
13984             // multiply and round.
13985             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13986             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13987             
13988             var mul = Math.pow(10, decimals);
13989             var zero = String(mul).substring(1);
13990             v = (Math.round((v-0)*mul))/mul;
13991             
13992             // if it's '0' number.. then
13993             
13994             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13995             v = String(v);
13996             var ps = v.split('.');
13997             var whole = ps[0];
13998             
13999             var r = /(\d+)(\d{3})/;
14000             // add comma's
14001             
14002             if(thousandsDelimiter.length != 0) {
14003                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14004             } 
14005             
14006             var sub = ps[1] ?
14007                     // has decimals..
14008                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14009                     // does not have decimals
14010                     (decimals ? ('.' + zero) : '');
14011             
14012             
14013             return whole + sub ;
14014         },
14015         
14016         /**
14017          * Parse a value into a formatted date using the specified format pattern.
14018          * @param {Mixed} value The value to format
14019          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14020          * @return {String} The formatted date string
14021          */
14022         date : function(v, format){
14023             if(!v){
14024                 return "";
14025             }
14026             if(!(v instanceof Date)){
14027                 v = new Date(Date.parse(v));
14028             }
14029             return v.dateFormat(format || Roo.util.Format.defaults.date);
14030         },
14031
14032         /**
14033          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14034          * @param {String} format Any valid date format string
14035          * @return {Function} The date formatting function
14036          */
14037         dateRenderer : function(format){
14038             return function(v){
14039                 return Roo.util.Format.date(v, format);  
14040             };
14041         },
14042
14043         // private
14044         stripTagsRE : /<\/?[^>]+>/gi,
14045         
14046         /**
14047          * Strips all HTML tags
14048          * @param {Mixed} value The text from which to strip tags
14049          * @return {String} The stripped text
14050          */
14051         stripTags : function(v){
14052             return !v ? v : String(v).replace(this.stripTagsRE, "");
14053         },
14054         
14055         /**
14056          * Size in Mb,Gb etc.
14057          * @param {Number} value The number to be formated
14058          * @param {number} decimals how many decimal places
14059          * @return {String} the formated string
14060          */
14061         size : function(value, decimals)
14062         {
14063             var sizes = ['b', 'k', 'M', 'G', 'T'];
14064             if (value == 0) {
14065                 return 0;
14066             }
14067             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14068             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14069         }
14070         
14071         
14072         
14073     };
14074 }();
14075 Roo.util.Format.defaults = {
14076     date : 'd/M/Y'
14077 };/*
14078  * Based on:
14079  * Ext JS Library 1.1.1
14080  * Copyright(c) 2006-2007, Ext JS, LLC.
14081  *
14082  * Originally Released Under LGPL - original licence link has changed is not relivant.
14083  *
14084  * Fork - LGPL
14085  * <script type="text/javascript">
14086  */
14087
14088
14089  
14090
14091 /**
14092  * @class Roo.MasterTemplate
14093  * @extends Roo.Template
14094  * Provides a template that can have child templates. The syntax is:
14095 <pre><code>
14096 var t = new Roo.MasterTemplate(
14097         '&lt;select name="{name}"&gt;',
14098                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14099         '&lt;/select&gt;'
14100 );
14101 t.add('options', {value: 'foo', text: 'bar'});
14102 // or you can add multiple child elements in one shot
14103 t.addAll('options', [
14104     {value: 'foo', text: 'bar'},
14105     {value: 'foo2', text: 'bar2'},
14106     {value: 'foo3', text: 'bar3'}
14107 ]);
14108 // then append, applying the master template values
14109 t.append('my-form', {name: 'my-select'});
14110 </code></pre>
14111 * A name attribute for the child template is not required if you have only one child
14112 * template or you want to refer to them by index.
14113  */
14114 Roo.MasterTemplate = function(){
14115     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14116     this.originalHtml = this.html;
14117     var st = {};
14118     var m, re = this.subTemplateRe;
14119     re.lastIndex = 0;
14120     var subIndex = 0;
14121     while(m = re.exec(this.html)){
14122         var name = m[1], content = m[2];
14123         st[subIndex] = {
14124             name: name,
14125             index: subIndex,
14126             buffer: [],
14127             tpl : new Roo.Template(content)
14128         };
14129         if(name){
14130             st[name] = st[subIndex];
14131         }
14132         st[subIndex].tpl.compile();
14133         st[subIndex].tpl.call = this.call.createDelegate(this);
14134         subIndex++;
14135     }
14136     this.subCount = subIndex;
14137     this.subs = st;
14138 };
14139 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14140     /**
14141     * The regular expression used to match sub templates
14142     * @type RegExp
14143     * @property
14144     */
14145     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14146
14147     /**
14148      * Applies the passed values to a child template.
14149      * @param {String/Number} name (optional) The name or index of the child template
14150      * @param {Array/Object} values The values to be applied to the template
14151      * @return {MasterTemplate} this
14152      */
14153      add : function(name, values){
14154         if(arguments.length == 1){
14155             values = arguments[0];
14156             name = 0;
14157         }
14158         var s = this.subs[name];
14159         s.buffer[s.buffer.length] = s.tpl.apply(values);
14160         return this;
14161     },
14162
14163     /**
14164      * Applies all the passed values to a child template.
14165      * @param {String/Number} name (optional) The name or index of the child template
14166      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14167      * @param {Boolean} reset (optional) True to reset the template first
14168      * @return {MasterTemplate} this
14169      */
14170     fill : function(name, values, reset){
14171         var a = arguments;
14172         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14173             values = a[0];
14174             name = 0;
14175             reset = a[1];
14176         }
14177         if(reset){
14178             this.reset();
14179         }
14180         for(var i = 0, len = values.length; i < len; i++){
14181             this.add(name, values[i]);
14182         }
14183         return this;
14184     },
14185
14186     /**
14187      * Resets the template for reuse
14188      * @return {MasterTemplate} this
14189      */
14190      reset : function(){
14191         var s = this.subs;
14192         for(var i = 0; i < this.subCount; i++){
14193             s[i].buffer = [];
14194         }
14195         return this;
14196     },
14197
14198     applyTemplate : function(values){
14199         var s = this.subs;
14200         var replaceIndex = -1;
14201         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14202             return s[++replaceIndex].buffer.join("");
14203         });
14204         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14205     },
14206
14207     apply : function(){
14208         return this.applyTemplate.apply(this, arguments);
14209     },
14210
14211     compile : function(){return this;}
14212 });
14213
14214 /**
14215  * Alias for fill().
14216  * @method
14217  */
14218 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14219  /**
14220  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14221  * var tpl = Roo.MasterTemplate.from('element-id');
14222  * @param {String/HTMLElement} el
14223  * @param {Object} config
14224  * @static
14225  */
14226 Roo.MasterTemplate.from = function(el, config){
14227     el = Roo.getDom(el);
14228     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14229 };/*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239
14240  
14241 /**
14242  * @class Roo.util.CSS
14243  * Utility class for manipulating CSS rules
14244  * @singleton
14245  */
14246 Roo.util.CSS = function(){
14247         var rules = null;
14248         var doc = document;
14249
14250     var camelRe = /(-[a-z])/gi;
14251     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14252
14253    return {
14254    /**
14255     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14256     * tag and appended to the HEAD of the document.
14257     * @param {String|Object} cssText The text containing the css rules
14258     * @param {String} id An id to add to the stylesheet for later removal
14259     * @return {StyleSheet}
14260     */
14261     createStyleSheet : function(cssText, id){
14262         var ss;
14263         var head = doc.getElementsByTagName("head")[0];
14264         var nrules = doc.createElement("style");
14265         nrules.setAttribute("type", "text/css");
14266         if(id){
14267             nrules.setAttribute("id", id);
14268         }
14269         if (typeof(cssText) != 'string') {
14270             // support object maps..
14271             // not sure if this a good idea.. 
14272             // perhaps it should be merged with the general css handling
14273             // and handle js style props.
14274             var cssTextNew = [];
14275             for(var n in cssText) {
14276                 var citems = [];
14277                 for(var k in cssText[n]) {
14278                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14279                 }
14280                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14281                 
14282             }
14283             cssText = cssTextNew.join("\n");
14284             
14285         }
14286        
14287        
14288        if(Roo.isIE){
14289            head.appendChild(nrules);
14290            ss = nrules.styleSheet;
14291            ss.cssText = cssText;
14292        }else{
14293            try{
14294                 nrules.appendChild(doc.createTextNode(cssText));
14295            }catch(e){
14296                nrules.cssText = cssText; 
14297            }
14298            head.appendChild(nrules);
14299            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14300        }
14301        this.cacheStyleSheet(ss);
14302        return ss;
14303    },
14304
14305    /**
14306     * Removes a style or link tag by id
14307     * @param {String} id The id of the tag
14308     */
14309    removeStyleSheet : function(id){
14310        var existing = doc.getElementById(id);
14311        if(existing){
14312            existing.parentNode.removeChild(existing);
14313        }
14314    },
14315
14316    /**
14317     * Dynamically swaps an existing stylesheet reference for a new one
14318     * @param {String} id The id of an existing link tag to remove
14319     * @param {String} url The href of the new stylesheet to include
14320     */
14321    swapStyleSheet : function(id, url){
14322        this.removeStyleSheet(id);
14323        var ss = doc.createElement("link");
14324        ss.setAttribute("rel", "stylesheet");
14325        ss.setAttribute("type", "text/css");
14326        ss.setAttribute("id", id);
14327        ss.setAttribute("href", url);
14328        doc.getElementsByTagName("head")[0].appendChild(ss);
14329    },
14330    
14331    /**
14332     * Refresh the rule cache if you have dynamically added stylesheets
14333     * @return {Object} An object (hash) of rules indexed by selector
14334     */
14335    refreshCache : function(){
14336        return this.getRules(true);
14337    },
14338
14339    // private
14340    cacheStyleSheet : function(stylesheet){
14341        if(!rules){
14342            rules = {};
14343        }
14344        try{// try catch for cross domain access issue
14345            var ssRules = stylesheet.cssRules || stylesheet.rules;
14346            for(var j = ssRules.length-1; j >= 0; --j){
14347                rules[ssRules[j].selectorText] = ssRules[j];
14348            }
14349        }catch(e){}
14350    },
14351    
14352    /**
14353     * Gets all css rules for the document
14354     * @param {Boolean} refreshCache true to refresh the internal cache
14355     * @return {Object} An object (hash) of rules indexed by selector
14356     */
14357    getRules : function(refreshCache){
14358                 if(rules == null || refreshCache){
14359                         rules = {};
14360                         var ds = doc.styleSheets;
14361                         for(var i =0, len = ds.length; i < len; i++){
14362                             try{
14363                         this.cacheStyleSheet(ds[i]);
14364                     }catch(e){} 
14365                 }
14366                 }
14367                 return rules;
14368         },
14369         
14370         /**
14371     * Gets an an individual CSS rule by selector(s)
14372     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14373     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14374     * @return {CSSRule} The CSS rule or null if one is not found
14375     */
14376    getRule : function(selector, refreshCache){
14377                 var rs = this.getRules(refreshCache);
14378                 if(!(selector instanceof Array)){
14379                     return rs[selector];
14380                 }
14381                 for(var i = 0; i < selector.length; i++){
14382                         if(rs[selector[i]]){
14383                                 return rs[selector[i]];
14384                         }
14385                 }
14386                 return null;
14387         },
14388         
14389         
14390         /**
14391     * Updates a rule property
14392     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14393     * @param {String} property The css property
14394     * @param {String} value The new value for the property
14395     * @return {Boolean} true If a rule was found and updated
14396     */
14397    updateRule : function(selector, property, value){
14398                 if(!(selector instanceof Array)){
14399                         var rule = this.getRule(selector);
14400                         if(rule){
14401                                 rule.style[property.replace(camelRe, camelFn)] = value;
14402                                 return true;
14403                         }
14404                 }else{
14405                         for(var i = 0; i < selector.length; i++){
14406                                 if(this.updateRule(selector[i], property, value)){
14407                                         return true;
14408                                 }
14409                         }
14410                 }
14411                 return false;
14412         }
14413    };   
14414 }();/*
14415  * Based on:
14416  * Ext JS Library 1.1.1
14417  * Copyright(c) 2006-2007, Ext JS, LLC.
14418  *
14419  * Originally Released Under LGPL - original licence link has changed is not relivant.
14420  *
14421  * Fork - LGPL
14422  * <script type="text/javascript">
14423  */
14424
14425  
14426
14427 /**
14428  * @class Roo.util.ClickRepeater
14429  * @extends Roo.util.Observable
14430  * 
14431  * A wrapper class which can be applied to any element. Fires a "click" event while the
14432  * mouse is pressed. The interval between firings may be specified in the config but
14433  * defaults to 10 milliseconds.
14434  * 
14435  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14436  * 
14437  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14438  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14439  * Similar to an autorepeat key delay.
14440  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14441  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14442  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14443  *           "interval" and "delay" are ignored. "immediate" is honored.
14444  * @cfg {Boolean} preventDefault True to prevent the default click event
14445  * @cfg {Boolean} stopDefault True to stop the default click event
14446  * 
14447  * @history
14448  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14449  *     2007-02-02 jvs Renamed to ClickRepeater
14450  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14451  *
14452  *  @constructor
14453  * @param {String/HTMLElement/Element} el The element to listen on
14454  * @param {Object} config
14455  **/
14456 Roo.util.ClickRepeater = function(el, config)
14457 {
14458     this.el = Roo.get(el);
14459     this.el.unselectable();
14460
14461     Roo.apply(this, config);
14462
14463     this.addEvents({
14464     /**
14465      * @event mousedown
14466      * Fires when the mouse button is depressed.
14467      * @param {Roo.util.ClickRepeater} this
14468      */
14469         "mousedown" : true,
14470     /**
14471      * @event click
14472      * Fires on a specified interval during the time the element is pressed.
14473      * @param {Roo.util.ClickRepeater} this
14474      */
14475         "click" : true,
14476     /**
14477      * @event mouseup
14478      * Fires when the mouse key is released.
14479      * @param {Roo.util.ClickRepeater} this
14480      */
14481         "mouseup" : true
14482     });
14483
14484     this.el.on("mousedown", this.handleMouseDown, this);
14485     if(this.preventDefault || this.stopDefault){
14486         this.el.on("click", function(e){
14487             if(this.preventDefault){
14488                 e.preventDefault();
14489             }
14490             if(this.stopDefault){
14491                 e.stopEvent();
14492             }
14493         }, this);
14494     }
14495
14496     // allow inline handler
14497     if(this.handler){
14498         this.on("click", this.handler,  this.scope || this);
14499     }
14500
14501     Roo.util.ClickRepeater.superclass.constructor.call(this);
14502 };
14503
14504 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14505     interval : 20,
14506     delay: 250,
14507     preventDefault : true,
14508     stopDefault : false,
14509     timer : 0,
14510
14511     // private
14512     handleMouseDown : function(){
14513         clearTimeout(this.timer);
14514         this.el.blur();
14515         if(this.pressClass){
14516             this.el.addClass(this.pressClass);
14517         }
14518         this.mousedownTime = new Date();
14519
14520         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14521         this.el.on("mouseout", this.handleMouseOut, this);
14522
14523         this.fireEvent("mousedown", this);
14524         this.fireEvent("click", this);
14525         
14526         this.timer = this.click.defer(this.delay || this.interval, this);
14527     },
14528
14529     // private
14530     click : function(){
14531         this.fireEvent("click", this);
14532         this.timer = this.click.defer(this.getInterval(), this);
14533     },
14534
14535     // private
14536     getInterval: function(){
14537         if(!this.accelerate){
14538             return this.interval;
14539         }
14540         var pressTime = this.mousedownTime.getElapsed();
14541         if(pressTime < 500){
14542             return 400;
14543         }else if(pressTime < 1700){
14544             return 320;
14545         }else if(pressTime < 2600){
14546             return 250;
14547         }else if(pressTime < 3500){
14548             return 180;
14549         }else if(pressTime < 4400){
14550             return 140;
14551         }else if(pressTime < 5300){
14552             return 80;
14553         }else if(pressTime < 6200){
14554             return 50;
14555         }else{
14556             return 10;
14557         }
14558     },
14559
14560     // private
14561     handleMouseOut : function(){
14562         clearTimeout(this.timer);
14563         if(this.pressClass){
14564             this.el.removeClass(this.pressClass);
14565         }
14566         this.el.on("mouseover", this.handleMouseReturn, this);
14567     },
14568
14569     // private
14570     handleMouseReturn : function(){
14571         this.el.un("mouseover", this.handleMouseReturn);
14572         if(this.pressClass){
14573             this.el.addClass(this.pressClass);
14574         }
14575         this.click();
14576     },
14577
14578     // private
14579     handleMouseUp : function(){
14580         clearTimeout(this.timer);
14581         this.el.un("mouseover", this.handleMouseReturn);
14582         this.el.un("mouseout", this.handleMouseOut);
14583         Roo.get(document).un("mouseup", this.handleMouseUp);
14584         this.el.removeClass(this.pressClass);
14585         this.fireEvent("mouseup", this);
14586     }
14587 });/**
14588  * @class Roo.util.Clipboard
14589  * @static
14590  * 
14591  * Clipboard UTILS
14592  * 
14593  **/
14594 Roo.util.Clipboard = {
14595     /**
14596      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14597      * @param {String} text to copy to clipboard
14598      */
14599     write : function(text) {
14600         // navigator clipboard api needs a secure context (https)
14601         if (navigator.clipboard && window.isSecureContext) {
14602             // navigator clipboard api method'
14603             navigator.clipboard.writeText(text);
14604             return ;
14605         } 
14606         // text area method
14607         var ta = document.createElement("textarea");
14608         ta.value = text;
14609         // make the textarea out of viewport
14610         ta.style.position = "fixed";
14611         ta.style.left = "-999999px";
14612         ta.style.top = "-999999px";
14613         document.body.appendChild(ta);
14614         ta.focus();
14615         ta.select();
14616         document.execCommand('copy');
14617         (function() {
14618             ta.remove();
14619         }).defer(100);
14620         
14621     }
14622         
14623 }
14624     /*
14625  * Based on:
14626  * Ext JS Library 1.1.1
14627  * Copyright(c) 2006-2007, Ext JS, LLC.
14628  *
14629  * Originally Released Under LGPL - original licence link has changed is not relivant.
14630  *
14631  * Fork - LGPL
14632  * <script type="text/javascript">
14633  */
14634
14635  
14636 /**
14637  * @class Roo.KeyNav
14638  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14639  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14640  * way to implement custom navigation schemes for any UI component.</p>
14641  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14642  * pageUp, pageDown, del, home, end.  Usage:</p>
14643  <pre><code>
14644 var nav = new Roo.KeyNav("my-element", {
14645     "left" : function(e){
14646         this.moveLeft(e.ctrlKey);
14647     },
14648     "right" : function(e){
14649         this.moveRight(e.ctrlKey);
14650     },
14651     "enter" : function(e){
14652         this.save();
14653     },
14654     scope : this
14655 });
14656 </code></pre>
14657  * @constructor
14658  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14659  * @param {Object} config The config
14660  */
14661 Roo.KeyNav = function(el, config){
14662     this.el = Roo.get(el);
14663     Roo.apply(this, config);
14664     if(!this.disabled){
14665         this.disabled = true;
14666         this.enable();
14667     }
14668 };
14669
14670 Roo.KeyNav.prototype = {
14671     /**
14672      * @cfg {Boolean} disabled
14673      * True to disable this KeyNav instance (defaults to false)
14674      */
14675     disabled : false,
14676     /**
14677      * @cfg {String} defaultEventAction
14678      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14679      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14680      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14681      */
14682     defaultEventAction: "stopEvent",
14683     /**
14684      * @cfg {Boolean} forceKeyDown
14685      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14686      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14687      * handle keydown instead of keypress.
14688      */
14689     forceKeyDown : false,
14690
14691     // private
14692     prepareEvent : function(e){
14693         var k = e.getKey();
14694         var h = this.keyToHandler[k];
14695         //if(h && this[h]){
14696         //    e.stopPropagation();
14697         //}
14698         if(Roo.isSafari && h && k >= 37 && k <= 40){
14699             e.stopEvent();
14700         }
14701     },
14702
14703     // private
14704     relay : function(e){
14705         var k = e.getKey();
14706         var h = this.keyToHandler[k];
14707         if(h && this[h]){
14708             if(this.doRelay(e, this[h], h) !== true){
14709                 e[this.defaultEventAction]();
14710             }
14711         }
14712     },
14713
14714     // private
14715     doRelay : function(e, h, hname){
14716         return h.call(this.scope || this, e);
14717     },
14718
14719     // possible handlers
14720     enter : false,
14721     left : false,
14722     right : false,
14723     up : false,
14724     down : false,
14725     tab : false,
14726     esc : false,
14727     pageUp : false,
14728     pageDown : false,
14729     del : false,
14730     home : false,
14731     end : false,
14732
14733     // quick lookup hash
14734     keyToHandler : {
14735         37 : "left",
14736         39 : "right",
14737         38 : "up",
14738         40 : "down",
14739         33 : "pageUp",
14740         34 : "pageDown",
14741         46 : "del",
14742         36 : "home",
14743         35 : "end",
14744         13 : "enter",
14745         27 : "esc",
14746         9  : "tab"
14747     },
14748
14749         /**
14750          * Enable this KeyNav
14751          */
14752         enable: function(){
14753                 if(this.disabled){
14754             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14755             // the EventObject will normalize Safari automatically
14756             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14757                 this.el.on("keydown", this.relay,  this);
14758             }else{
14759                 this.el.on("keydown", this.prepareEvent,  this);
14760                 this.el.on("keypress", this.relay,  this);
14761             }
14762                     this.disabled = false;
14763                 }
14764         },
14765
14766         /**
14767          * Disable this KeyNav
14768          */
14769         disable: function(){
14770                 if(!this.disabled){
14771                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14772                 this.el.un("keydown", this.relay);
14773             }else{
14774                 this.el.un("keydown", this.prepareEvent);
14775                 this.el.un("keypress", this.relay);
14776             }
14777                     this.disabled = true;
14778                 }
14779         }
14780 };/*
14781  * Based on:
14782  * Ext JS Library 1.1.1
14783  * Copyright(c) 2006-2007, Ext JS, LLC.
14784  *
14785  * Originally Released Under LGPL - original licence link has changed is not relivant.
14786  *
14787  * Fork - LGPL
14788  * <script type="text/javascript">
14789  */
14790
14791  
14792 /**
14793  * @class Roo.KeyMap
14794  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14795  * The constructor accepts the same config object as defined by {@link #addBinding}.
14796  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14797  * combination it will call the function with this signature (if the match is a multi-key
14798  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14799  * A KeyMap can also handle a string representation of keys.<br />
14800  * Usage:
14801  <pre><code>
14802 // map one key by key code
14803 var map = new Roo.KeyMap("my-element", {
14804     key: 13, // or Roo.EventObject.ENTER
14805     fn: myHandler,
14806     scope: myObject
14807 });
14808
14809 // map multiple keys to one action by string
14810 var map = new Roo.KeyMap("my-element", {
14811     key: "a\r\n\t",
14812     fn: myHandler,
14813     scope: myObject
14814 });
14815
14816 // map multiple keys to multiple actions by strings and array of codes
14817 var map = new Roo.KeyMap("my-element", [
14818     {
14819         key: [10,13],
14820         fn: function(){ alert("Return was pressed"); }
14821     }, {
14822         key: "abc",
14823         fn: function(){ alert('a, b or c was pressed'); }
14824     }, {
14825         key: "\t",
14826         ctrl:true,
14827         shift:true,
14828         fn: function(){ alert('Control + shift + tab was pressed.'); }
14829     }
14830 ]);
14831 </code></pre>
14832  * <b>Note: A KeyMap starts enabled</b>
14833  * @constructor
14834  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14835  * @param {Object} config The config (see {@link #addBinding})
14836  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14837  */
14838 Roo.KeyMap = function(el, config, eventName){
14839     this.el  = Roo.get(el);
14840     this.eventName = eventName || "keydown";
14841     this.bindings = [];
14842     if(config){
14843         this.addBinding(config);
14844     }
14845     this.enable();
14846 };
14847
14848 Roo.KeyMap.prototype = {
14849     /**
14850      * True to stop the event from bubbling and prevent the default browser action if the
14851      * key was handled by the KeyMap (defaults to false)
14852      * @type Boolean
14853      */
14854     stopEvent : false,
14855
14856     /**
14857      * Add a new binding to this KeyMap. The following config object properties are supported:
14858      * <pre>
14859 Property    Type             Description
14860 ----------  ---------------  ----------------------------------------------------------------------
14861 key         String/Array     A single keycode or an array of keycodes to handle
14862 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14863 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14864 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14865 fn          Function         The function to call when KeyMap finds the expected key combination
14866 scope       Object           The scope of the callback function
14867 </pre>
14868      *
14869      * Usage:
14870      * <pre><code>
14871 // Create a KeyMap
14872 var map = new Roo.KeyMap(document, {
14873     key: Roo.EventObject.ENTER,
14874     fn: handleKey,
14875     scope: this
14876 });
14877
14878 //Add a new binding to the existing KeyMap later
14879 map.addBinding({
14880     key: 'abc',
14881     shift: true,
14882     fn: handleKey,
14883     scope: this
14884 });
14885 </code></pre>
14886      * @param {Object/Array} config A single KeyMap config or an array of configs
14887      */
14888         addBinding : function(config){
14889         if(config instanceof Array){
14890             for(var i = 0, len = config.length; i < len; i++){
14891                 this.addBinding(config[i]);
14892             }
14893             return;
14894         }
14895         var keyCode = config.key,
14896             shift = config.shift, 
14897             ctrl = config.ctrl, 
14898             alt = config.alt,
14899             fn = config.fn,
14900             scope = config.scope;
14901         if(typeof keyCode == "string"){
14902             var ks = [];
14903             var keyString = keyCode.toUpperCase();
14904             for(var j = 0, len = keyString.length; j < len; j++){
14905                 ks.push(keyString.charCodeAt(j));
14906             }
14907             keyCode = ks;
14908         }
14909         var keyArray = keyCode instanceof Array;
14910         var handler = function(e){
14911             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14912                 var k = e.getKey();
14913                 if(keyArray){
14914                     for(var i = 0, len = keyCode.length; i < len; i++){
14915                         if(keyCode[i] == k){
14916                           if(this.stopEvent){
14917                               e.stopEvent();
14918                           }
14919                           fn.call(scope || window, k, e);
14920                           return;
14921                         }
14922                     }
14923                 }else{
14924                     if(k == keyCode){
14925                         if(this.stopEvent){
14926                            e.stopEvent();
14927                         }
14928                         fn.call(scope || window, k, e);
14929                     }
14930                 }
14931             }
14932         };
14933         this.bindings.push(handler);  
14934         },
14935
14936     /**
14937      * Shorthand for adding a single key listener
14938      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14939      * following options:
14940      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14941      * @param {Function} fn The function to call
14942      * @param {Object} scope (optional) The scope of the function
14943      */
14944     on : function(key, fn, scope){
14945         var keyCode, shift, ctrl, alt;
14946         if(typeof key == "object" && !(key instanceof Array)){
14947             keyCode = key.key;
14948             shift = key.shift;
14949             ctrl = key.ctrl;
14950             alt = key.alt;
14951         }else{
14952             keyCode = key;
14953         }
14954         this.addBinding({
14955             key: keyCode,
14956             shift: shift,
14957             ctrl: ctrl,
14958             alt: alt,
14959             fn: fn,
14960             scope: scope
14961         })
14962     },
14963
14964     // private
14965     handleKeyDown : function(e){
14966             if(this.enabled){ //just in case
14967             var b = this.bindings;
14968             for(var i = 0, len = b.length; i < len; i++){
14969                 b[i].call(this, e);
14970             }
14971             }
14972         },
14973         
14974         /**
14975          * Returns true if this KeyMap is enabled
14976          * @return {Boolean} 
14977          */
14978         isEnabled : function(){
14979             return this.enabled;  
14980         },
14981         
14982         /**
14983          * Enables this KeyMap
14984          */
14985         enable: function(){
14986                 if(!this.enabled){
14987                     this.el.on(this.eventName, this.handleKeyDown, this);
14988                     this.enabled = true;
14989                 }
14990         },
14991
14992         /**
14993          * Disable this KeyMap
14994          */
14995         disable: function(){
14996                 if(this.enabled){
14997                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14998                     this.enabled = false;
14999                 }
15000         }
15001 };/*
15002  * Based on:
15003  * Ext JS Library 1.1.1
15004  * Copyright(c) 2006-2007, Ext JS, LLC.
15005  *
15006  * Originally Released Under LGPL - original licence link has changed is not relivant.
15007  *
15008  * Fork - LGPL
15009  * <script type="text/javascript">
15010  */
15011
15012  
15013 /**
15014  * @class Roo.util.TextMetrics
15015  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15016  * wide, in pixels, a given block of text will be.
15017  * @singleton
15018  */
15019 Roo.util.TextMetrics = function(){
15020     var shared;
15021     return {
15022         /**
15023          * Measures the size of the specified text
15024          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15025          * that can affect the size of the rendered text
15026          * @param {String} text The text to measure
15027          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15028          * in order to accurately measure the text height
15029          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15030          */
15031         measure : function(el, text, fixedWidth){
15032             if(!shared){
15033                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15034             }
15035             shared.bind(el);
15036             shared.setFixedWidth(fixedWidth || 'auto');
15037             return shared.getSize(text);
15038         },
15039
15040         /**
15041          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15042          * the overhead of multiple calls to initialize the style properties on each measurement.
15043          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15044          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15045          * in order to accurately measure the text height
15046          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15047          */
15048         createInstance : function(el, fixedWidth){
15049             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15050         }
15051     };
15052 }();
15053
15054  
15055
15056 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15057     var ml = new Roo.Element(document.createElement('div'));
15058     document.body.appendChild(ml.dom);
15059     ml.position('absolute');
15060     ml.setLeftTop(-1000, -1000);
15061     ml.hide();
15062
15063     if(fixedWidth){
15064         ml.setWidth(fixedWidth);
15065     }
15066      
15067     var instance = {
15068         /**
15069          * Returns the size of the specified text based on the internal element's style and width properties
15070          * @memberOf Roo.util.TextMetrics.Instance#
15071          * @param {String} text The text to measure
15072          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15073          */
15074         getSize : function(text){
15075             ml.update(text);
15076             var s = ml.getSize();
15077             ml.update('');
15078             return s;
15079         },
15080
15081         /**
15082          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15083          * that can affect the size of the rendered text
15084          * @memberOf Roo.util.TextMetrics.Instance#
15085          * @param {String/HTMLElement} el The element, dom node or id
15086          */
15087         bind : function(el){
15088             ml.setStyle(
15089                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15090             );
15091         },
15092
15093         /**
15094          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15095          * to set a fixed width in order to accurately measure the text height.
15096          * @memberOf Roo.util.TextMetrics.Instance#
15097          * @param {Number} width The width to set on the element
15098          */
15099         setFixedWidth : function(width){
15100             ml.setWidth(width);
15101         },
15102
15103         /**
15104          * Returns the measured width of the specified text
15105          * @memberOf Roo.util.TextMetrics.Instance#
15106          * @param {String} text The text to measure
15107          * @return {Number} width The width in pixels
15108          */
15109         getWidth : function(text){
15110             ml.dom.style.width = 'auto';
15111             return this.getSize(text).width;
15112         },
15113
15114         /**
15115          * Returns the measured height of the specified text.  For multiline text, be sure to call
15116          * {@link #setFixedWidth} if necessary.
15117          * @memberOf Roo.util.TextMetrics.Instance#
15118          * @param {String} text The text to measure
15119          * @return {Number} height The height in pixels
15120          */
15121         getHeight : function(text){
15122             return this.getSize(text).height;
15123         }
15124     };
15125
15126     instance.bind(bindTo);
15127
15128     return instance;
15129 };
15130
15131 // backwards compat
15132 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15133  * Based on:
15134  * Ext JS Library 1.1.1
15135  * Copyright(c) 2006-2007, Ext JS, LLC.
15136  *
15137  * Originally Released Under LGPL - original licence link has changed is not relivant.
15138  *
15139  * Fork - LGPL
15140  * <script type="text/javascript">
15141  */
15142
15143 /**
15144  * @class Roo.state.Provider
15145  * Abstract base class for state provider implementations. This class provides methods
15146  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15147  * Provider interface.
15148  */
15149 Roo.state.Provider = function(){
15150     /**
15151      * @event statechange
15152      * Fires when a state change occurs.
15153      * @param {Provider} this This state provider
15154      * @param {String} key The state key which was changed
15155      * @param {String} value The encoded value for the state
15156      */
15157     this.addEvents({
15158         "statechange": true
15159     });
15160     this.state = {};
15161     Roo.state.Provider.superclass.constructor.call(this);
15162 };
15163 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15164     /**
15165      * Returns the current value for a key
15166      * @param {String} name The key name
15167      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15168      * @return {Mixed} The state data
15169      */
15170     get : function(name, defaultValue){
15171         return typeof this.state[name] == "undefined" ?
15172             defaultValue : this.state[name];
15173     },
15174     
15175     /**
15176      * Clears a value from the state
15177      * @param {String} name The key name
15178      */
15179     clear : function(name){
15180         delete this.state[name];
15181         this.fireEvent("statechange", this, name, null);
15182     },
15183     
15184     /**
15185      * Sets the value for a key
15186      * @param {String} name The key name
15187      * @param {Mixed} value The value to set
15188      */
15189     set : function(name, value){
15190         this.state[name] = value;
15191         this.fireEvent("statechange", this, name, value);
15192     },
15193     
15194     /**
15195      * Decodes a string previously encoded with {@link #encodeValue}.
15196      * @param {String} value The value to decode
15197      * @return {Mixed} The decoded value
15198      */
15199     decodeValue : function(cookie){
15200         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15201         var matches = re.exec(unescape(cookie));
15202         if(!matches || !matches[1]) {
15203             return; // non state cookie
15204         }
15205         var type = matches[1];
15206         var v = matches[2];
15207         switch(type){
15208             case "n":
15209                 return parseFloat(v);
15210             case "d":
15211                 return new Date(Date.parse(v));
15212             case "b":
15213                 return (v == "1");
15214             case "a":
15215                 var all = [];
15216                 var values = v.split("^");
15217                 for(var i = 0, len = values.length; i < len; i++){
15218                     all.push(this.decodeValue(values[i]));
15219                 }
15220                 return all;
15221            case "o":
15222                 var all = {};
15223                 var values = v.split("^");
15224                 for(var i = 0, len = values.length; i < len; i++){
15225                     var kv = values[i].split("=");
15226                     all[kv[0]] = this.decodeValue(kv[1]);
15227                 }
15228                 return all;
15229            default:
15230                 return v;
15231         }
15232     },
15233     
15234     /**
15235      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15236      * @param {Mixed} value The value to encode
15237      * @return {String} The encoded value
15238      */
15239     encodeValue : function(v){
15240         var enc;
15241         if(typeof v == "number"){
15242             enc = "n:" + v;
15243         }else if(typeof v == "boolean"){
15244             enc = "b:" + (v ? "1" : "0");
15245         }else if(v instanceof Date){
15246             enc = "d:" + v.toGMTString();
15247         }else if(v instanceof Array){
15248             var flat = "";
15249             for(var i = 0, len = v.length; i < len; i++){
15250                 flat += this.encodeValue(v[i]);
15251                 if(i != len-1) {
15252                     flat += "^";
15253                 }
15254             }
15255             enc = "a:" + flat;
15256         }else if(typeof v == "object"){
15257             var flat = "";
15258             for(var key in v){
15259                 if(typeof v[key] != "function"){
15260                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15261                 }
15262             }
15263             enc = "o:" + flat.substring(0, flat.length-1);
15264         }else{
15265             enc = "s:" + v;
15266         }
15267         return escape(enc);        
15268     }
15269 });
15270
15271 /*
15272  * Based on:
15273  * Ext JS Library 1.1.1
15274  * Copyright(c) 2006-2007, Ext JS, LLC.
15275  *
15276  * Originally Released Under LGPL - original licence link has changed is not relivant.
15277  *
15278  * Fork - LGPL
15279  * <script type="text/javascript">
15280  */
15281 /**
15282  * @class Roo.state.Manager
15283  * This is the global state manager. By default all components that are "state aware" check this class
15284  * for state information if you don't pass them a custom state provider. In order for this class
15285  * to be useful, it must be initialized with a provider when your application initializes.
15286  <pre><code>
15287 // in your initialization function
15288 init : function(){
15289    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15290    ...
15291    // supposed you have a {@link Roo.BorderLayout}
15292    var layout = new Roo.BorderLayout(...);
15293    layout.restoreState();
15294    // or a {Roo.BasicDialog}
15295    var dialog = new Roo.BasicDialog(...);
15296    dialog.restoreState();
15297  </code></pre>
15298  * @singleton
15299  */
15300 Roo.state.Manager = function(){
15301     var provider = new Roo.state.Provider();
15302     
15303     return {
15304         /**
15305          * Configures the default state provider for your application
15306          * @param {Provider} stateProvider The state provider to set
15307          */
15308         setProvider : function(stateProvider){
15309             provider = stateProvider;
15310         },
15311         
15312         /**
15313          * Returns the current value for a key
15314          * @param {String} name The key name
15315          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15316          * @return {Mixed} The state data
15317          */
15318         get : function(key, defaultValue){
15319             return provider.get(key, defaultValue);
15320         },
15321         
15322         /**
15323          * Sets the value for a key
15324          * @param {String} name The key name
15325          * @param {Mixed} value The state data
15326          */
15327          set : function(key, value){
15328             provider.set(key, value);
15329         },
15330         
15331         /**
15332          * Clears a value from the state
15333          * @param {String} name The key name
15334          */
15335         clear : function(key){
15336             provider.clear(key);
15337         },
15338         
15339         /**
15340          * Gets the currently configured state provider
15341          * @return {Provider} The state provider
15342          */
15343         getProvider : function(){
15344             return provider;
15345         }
15346     };
15347 }();
15348 /*
15349  * Based on:
15350  * Ext JS Library 1.1.1
15351  * Copyright(c) 2006-2007, Ext JS, LLC.
15352  *
15353  * Originally Released Under LGPL - original licence link has changed is not relivant.
15354  *
15355  * Fork - LGPL
15356  * <script type="text/javascript">
15357  */
15358 /**
15359  * @class Roo.state.CookieProvider
15360  * @extends Roo.state.Provider
15361  * The default Provider implementation which saves state via cookies.
15362  * <br />Usage:
15363  <pre><code>
15364    var cp = new Roo.state.CookieProvider({
15365        path: "/cgi-bin/",
15366        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15367        domain: "roojs.com"
15368    })
15369    Roo.state.Manager.setProvider(cp);
15370  </code></pre>
15371  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15372  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15373  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15374  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15375  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15376  * domain the page is running on including the 'www' like 'www.roojs.com')
15377  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15378  * @constructor
15379  * Create a new CookieProvider
15380  * @param {Object} config The configuration object
15381  */
15382 Roo.state.CookieProvider = function(config){
15383     Roo.state.CookieProvider.superclass.constructor.call(this);
15384     this.path = "/";
15385     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15386     this.domain = null;
15387     this.secure = false;
15388     Roo.apply(this, config);
15389     this.state = this.readCookies();
15390 };
15391
15392 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15393     // private
15394     set : function(name, value){
15395         if(typeof value == "undefined" || value === null){
15396             this.clear(name);
15397             return;
15398         }
15399         this.setCookie(name, value);
15400         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15401     },
15402
15403     // private
15404     clear : function(name){
15405         this.clearCookie(name);
15406         Roo.state.CookieProvider.superclass.clear.call(this, name);
15407     },
15408
15409     // private
15410     readCookies : function(){
15411         var cookies = {};
15412         var c = document.cookie + ";";
15413         var re = /\s?(.*?)=(.*?);/g;
15414         var matches;
15415         while((matches = re.exec(c)) != null){
15416             var name = matches[1];
15417             var value = matches[2];
15418             if(name && name.substring(0,3) == "ys-"){
15419                 cookies[name.substr(3)] = this.decodeValue(value);
15420             }
15421         }
15422         return cookies;
15423     },
15424
15425     // private
15426     setCookie : function(name, value){
15427         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15428            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15429            ((this.path == null) ? "" : ("; path=" + this.path)) +
15430            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15431            ((this.secure == true) ? "; secure" : "");
15432     },
15433
15434     // private
15435     clearCookie : function(name){
15436         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15437            ((this.path == null) ? "" : ("; path=" + this.path)) +
15438            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15439            ((this.secure == true) ? "; secure" : "");
15440     }
15441 });/*
15442  * Based on:
15443  * Ext JS Library 1.1.1
15444  * Copyright(c) 2006-2007, Ext JS, LLC.
15445  *
15446  * Originally Released Under LGPL - original licence link has changed is not relivant.
15447  *
15448  * Fork - LGPL
15449  * <script type="text/javascript">
15450  */
15451  
15452
15453 /**
15454  * @class Roo.ComponentMgr
15455  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15456  * @singleton
15457  */
15458 Roo.ComponentMgr = function(){
15459     var all = new Roo.util.MixedCollection();
15460
15461     return {
15462         /**
15463          * Registers a component.
15464          * @param {Roo.Component} c The component
15465          */
15466         register : function(c){
15467             all.add(c);
15468         },
15469
15470         /**
15471          * Unregisters a component.
15472          * @param {Roo.Component} c The component
15473          */
15474         unregister : function(c){
15475             all.remove(c);
15476         },
15477
15478         /**
15479          * Returns a component by id
15480          * @param {String} id The component id
15481          */
15482         get : function(id){
15483             return all.get(id);
15484         },
15485
15486         /**
15487          * Registers a function that will be called when a specified component is added to ComponentMgr
15488          * @param {String} id The component id
15489          * @param {Funtction} fn The callback function
15490          * @param {Object} scope The scope of the callback
15491          */
15492         onAvailable : function(id, fn, scope){
15493             all.on("add", function(index, o){
15494                 if(o.id == id){
15495                     fn.call(scope || o, o);
15496                     all.un("add", fn, scope);
15497                 }
15498             });
15499         }
15500     };
15501 }();/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512 /**
15513  * @class Roo.Component
15514  * @extends Roo.util.Observable
15515  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15516  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15517  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15518  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15519  * All visual components (widgets) that require rendering into a layout should subclass Component.
15520  * @constructor
15521  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15522  * 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
15523  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15524  */
15525 Roo.Component = function(config){
15526     config = config || {};
15527     if(config.tagName || config.dom || typeof config == "string"){ // element object
15528         config = {el: config, id: config.id || config};
15529     }
15530     this.initialConfig = config;
15531
15532     Roo.apply(this, config);
15533     this.addEvents({
15534         /**
15535          * @event disable
15536          * Fires after the component is disabled.
15537              * @param {Roo.Component} this
15538              */
15539         disable : true,
15540         /**
15541          * @event enable
15542          * Fires after the component is enabled.
15543              * @param {Roo.Component} this
15544              */
15545         enable : true,
15546         /**
15547          * @event beforeshow
15548          * Fires before the component is shown.  Return false to stop the show.
15549              * @param {Roo.Component} this
15550              */
15551         beforeshow : true,
15552         /**
15553          * @event show
15554          * Fires after the component is shown.
15555              * @param {Roo.Component} this
15556              */
15557         show : true,
15558         /**
15559          * @event beforehide
15560          * Fires before the component is hidden. Return false to stop the hide.
15561              * @param {Roo.Component} this
15562              */
15563         beforehide : true,
15564         /**
15565          * @event hide
15566          * Fires after the component is hidden.
15567              * @param {Roo.Component} this
15568              */
15569         hide : true,
15570         /**
15571          * @event beforerender
15572          * Fires before the component is rendered. Return false to stop the render.
15573              * @param {Roo.Component} this
15574              */
15575         beforerender : true,
15576         /**
15577          * @event render
15578          * Fires after the component is rendered.
15579              * @param {Roo.Component} this
15580              */
15581         render : true,
15582         /**
15583          * @event beforedestroy
15584          * Fires before the component is destroyed. Return false to stop the destroy.
15585              * @param {Roo.Component} this
15586              */
15587         beforedestroy : true,
15588         /**
15589          * @event destroy
15590          * Fires after the component is destroyed.
15591              * @param {Roo.Component} this
15592              */
15593         destroy : true
15594     });
15595     if(!this.id){
15596         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15597     }
15598     Roo.ComponentMgr.register(this);
15599     Roo.Component.superclass.constructor.call(this);
15600     this.initComponent();
15601     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15602         this.render(this.renderTo);
15603         delete this.renderTo;
15604     }
15605 };
15606
15607 /** @private */
15608 Roo.Component.AUTO_ID = 1000;
15609
15610 Roo.extend(Roo.Component, Roo.util.Observable, {
15611     /**
15612      * @scope Roo.Component.prototype
15613      * @type {Boolean}
15614      * true if this component is hidden. Read-only.
15615      */
15616     hidden : false,
15617     /**
15618      * @type {Boolean}
15619      * true if this component is disabled. Read-only.
15620      */
15621     disabled : false,
15622     /**
15623      * @type {Boolean}
15624      * true if this component has been rendered. Read-only.
15625      */
15626     rendered : false,
15627     
15628     /** @cfg {String} disableClass
15629      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15630      */
15631     disabledClass : "x-item-disabled",
15632         /** @cfg {Boolean} allowDomMove
15633          * Whether the component can move the Dom node when rendering (defaults to true).
15634          */
15635     allowDomMove : true,
15636     /** @cfg {String} hideMode (display|visibility)
15637      * How this component should hidden. Supported values are
15638      * "visibility" (css visibility), "offsets" (negative offset position) and
15639      * "display" (css display) - defaults to "display".
15640      */
15641     hideMode: 'display',
15642
15643     /** @private */
15644     ctype : "Roo.Component",
15645
15646     /**
15647      * @cfg {String} actionMode 
15648      * which property holds the element that used for  hide() / show() / disable() / enable()
15649      * default is 'el' for forms you probably want to set this to fieldEl 
15650      */
15651     actionMode : "el",
15652
15653     /** @private */
15654     getActionEl : function(){
15655         return this[this.actionMode];
15656     },
15657
15658     initComponent : Roo.emptyFn,
15659     /**
15660      * If this is a lazy rendering component, render it to its container element.
15661      * @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.
15662      */
15663     render : function(container, position){
15664         
15665         if(this.rendered){
15666             return this;
15667         }
15668         
15669         if(this.fireEvent("beforerender", this) === false){
15670             return false;
15671         }
15672         
15673         if(!container && this.el){
15674             this.el = Roo.get(this.el);
15675             container = this.el.dom.parentNode;
15676             this.allowDomMove = false;
15677         }
15678         this.container = Roo.get(container);
15679         this.rendered = true;
15680         if(position !== undefined){
15681             if(typeof position == 'number'){
15682                 position = this.container.dom.childNodes[position];
15683             }else{
15684                 position = Roo.getDom(position);
15685             }
15686         }
15687         this.onRender(this.container, position || null);
15688         if(this.cls){
15689             this.el.addClass(this.cls);
15690             delete this.cls;
15691         }
15692         if(this.style){
15693             this.el.applyStyles(this.style);
15694             delete this.style;
15695         }
15696         this.fireEvent("render", this);
15697         this.afterRender(this.container);
15698         if(this.hidden){
15699             this.hide();
15700         }
15701         if(this.disabled){
15702             this.disable();
15703         }
15704
15705         return this;
15706         
15707     },
15708
15709     /** @private */
15710     // default function is not really useful
15711     onRender : function(ct, position){
15712         if(this.el){
15713             this.el = Roo.get(this.el);
15714             if(this.allowDomMove !== false){
15715                 ct.dom.insertBefore(this.el.dom, position);
15716             }
15717         }
15718     },
15719
15720     /** @private */
15721     getAutoCreate : function(){
15722         var cfg = typeof this.autoCreate == "object" ?
15723                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15724         if(this.id && !cfg.id){
15725             cfg.id = this.id;
15726         }
15727         return cfg;
15728     },
15729
15730     /** @private */
15731     afterRender : Roo.emptyFn,
15732
15733     /**
15734      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15735      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15736      */
15737     destroy : function(){
15738         if(this.fireEvent("beforedestroy", this) !== false){
15739             this.purgeListeners();
15740             this.beforeDestroy();
15741             if(this.rendered){
15742                 this.el.removeAllListeners();
15743                 this.el.remove();
15744                 if(this.actionMode == "container"){
15745                     this.container.remove();
15746                 }
15747             }
15748             this.onDestroy();
15749             Roo.ComponentMgr.unregister(this);
15750             this.fireEvent("destroy", this);
15751         }
15752     },
15753
15754         /** @private */
15755     beforeDestroy : function(){
15756
15757     },
15758
15759         /** @private */
15760         onDestroy : function(){
15761
15762     },
15763
15764     /**
15765      * Returns the underlying {@link Roo.Element}.
15766      * @return {Roo.Element} The element
15767      */
15768     getEl : function(){
15769         return this.el;
15770     },
15771
15772     /**
15773      * Returns the id of this component.
15774      * @return {String}
15775      */
15776     getId : function(){
15777         return this.id;
15778     },
15779
15780     /**
15781      * Try to focus this component.
15782      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15783      * @return {Roo.Component} this
15784      */
15785     focus : function(selectText){
15786         if(this.rendered){
15787             this.el.focus();
15788             if(selectText === true){
15789                 this.el.dom.select();
15790             }
15791         }
15792         return this;
15793     },
15794
15795     /** @private */
15796     blur : function(){
15797         if(this.rendered){
15798             this.el.blur();
15799         }
15800         return this;
15801     },
15802
15803     /**
15804      * Disable this component.
15805      * @return {Roo.Component} this
15806      */
15807     disable : function(){
15808         if(this.rendered){
15809             this.onDisable();
15810         }
15811         this.disabled = true;
15812         this.fireEvent("disable", this);
15813         return this;
15814     },
15815
15816         // private
15817     onDisable : function(){
15818         this.getActionEl().addClass(this.disabledClass);
15819         this.el.dom.disabled = true;
15820     },
15821
15822     /**
15823      * Enable this component.
15824      * @return {Roo.Component} this
15825      */
15826     enable : function(){
15827         if(this.rendered){
15828             this.onEnable();
15829         }
15830         this.disabled = false;
15831         this.fireEvent("enable", this);
15832         return this;
15833     },
15834
15835         // private
15836     onEnable : function(){
15837         this.getActionEl().removeClass(this.disabledClass);
15838         this.el.dom.disabled = false;
15839     },
15840
15841     /**
15842      * Convenience function for setting disabled/enabled by boolean.
15843      * @param {Boolean} disabled
15844      */
15845     setDisabled : function(disabled){
15846         this[disabled ? "disable" : "enable"]();
15847     },
15848
15849     /**
15850      * Show this component.
15851      * @return {Roo.Component} this
15852      */
15853     show: function(){
15854         if(this.fireEvent("beforeshow", this) !== false){
15855             this.hidden = false;
15856             if(this.rendered){
15857                 this.onShow();
15858             }
15859             this.fireEvent("show", this);
15860         }
15861         return this;
15862     },
15863
15864     // private
15865     onShow : function(){
15866         var ae = this.getActionEl();
15867         if(this.hideMode == 'visibility'){
15868             ae.dom.style.visibility = "visible";
15869         }else if(this.hideMode == 'offsets'){
15870             ae.removeClass('x-hidden');
15871         }else{
15872             ae.dom.style.display = "";
15873         }
15874     },
15875
15876     /**
15877      * Hide this component.
15878      * @return {Roo.Component} this
15879      */
15880     hide: function(){
15881         if(this.fireEvent("beforehide", this) !== false){
15882             this.hidden = true;
15883             if(this.rendered){
15884                 this.onHide();
15885             }
15886             this.fireEvent("hide", this);
15887         }
15888         return this;
15889     },
15890
15891     // private
15892     onHide : function(){
15893         var ae = this.getActionEl();
15894         if(this.hideMode == 'visibility'){
15895             ae.dom.style.visibility = "hidden";
15896         }else if(this.hideMode == 'offsets'){
15897             ae.addClass('x-hidden');
15898         }else{
15899             ae.dom.style.display = "none";
15900         }
15901     },
15902
15903     /**
15904      * Convenience function to hide or show this component by boolean.
15905      * @param {Boolean} visible True to show, false to hide
15906      * @return {Roo.Component} this
15907      */
15908     setVisible: function(visible){
15909         if(visible) {
15910             this.show();
15911         }else{
15912             this.hide();
15913         }
15914         return this;
15915     },
15916
15917     /**
15918      * Returns true if this component is visible.
15919      */
15920     isVisible : function(){
15921         return this.getActionEl().isVisible();
15922     },
15923
15924     cloneConfig : function(overrides){
15925         overrides = overrides || {};
15926         var id = overrides.id || Roo.id();
15927         var cfg = Roo.applyIf(overrides, this.initialConfig);
15928         cfg.id = id; // prevent dup id
15929         return new this.constructor(cfg);
15930     }
15931 });/*
15932  * Based on:
15933  * Ext JS Library 1.1.1
15934  * Copyright(c) 2006-2007, Ext JS, LLC.
15935  *
15936  * Originally Released Under LGPL - original licence link has changed is not relivant.
15937  *
15938  * Fork - LGPL
15939  * <script type="text/javascript">
15940  */
15941
15942 /**
15943  * @class Roo.BoxComponent
15944  * @extends Roo.Component
15945  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15946  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15947  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15948  * layout containers.
15949  * @constructor
15950  * @param {Roo.Element/String/Object} config The configuration options.
15951  */
15952 Roo.BoxComponent = function(config){
15953     Roo.Component.call(this, config);
15954     this.addEvents({
15955         /**
15956          * @event resize
15957          * Fires after the component is resized.
15958              * @param {Roo.Component} this
15959              * @param {Number} adjWidth The box-adjusted width that was set
15960              * @param {Number} adjHeight The box-adjusted height that was set
15961              * @param {Number} rawWidth The width that was originally specified
15962              * @param {Number} rawHeight The height that was originally specified
15963              */
15964         resize : true,
15965         /**
15966          * @event move
15967          * Fires after the component is moved.
15968              * @param {Roo.Component} this
15969              * @param {Number} x The new x position
15970              * @param {Number} y The new y position
15971              */
15972         move : true
15973     });
15974 };
15975
15976 Roo.extend(Roo.BoxComponent, Roo.Component, {
15977     // private, set in afterRender to signify that the component has been rendered
15978     boxReady : false,
15979     // private, used to defer height settings to subclasses
15980     deferHeight: false,
15981     /** @cfg {Number} width
15982      * width (optional) size of component
15983      */
15984      /** @cfg {Number} height
15985      * height (optional) size of component
15986      */
15987      
15988     /**
15989      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15990      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15991      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15992      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15993      * @return {Roo.BoxComponent} this
15994      */
15995     setSize : function(w, h){
15996         // support for standard size objects
15997         if(typeof w == 'object'){
15998             h = w.height;
15999             w = w.width;
16000         }
16001         // not rendered
16002         if(!this.boxReady){
16003             this.width = w;
16004             this.height = h;
16005             return this;
16006         }
16007
16008         // prevent recalcs when not needed
16009         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16010             return this;
16011         }
16012         this.lastSize = {width: w, height: h};
16013
16014         var adj = this.adjustSize(w, h);
16015         var aw = adj.width, ah = adj.height;
16016         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16017             var rz = this.getResizeEl();
16018             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16019                 rz.setSize(aw, ah);
16020             }else if(!this.deferHeight && ah !== undefined){
16021                 rz.setHeight(ah);
16022             }else if(aw !== undefined){
16023                 rz.setWidth(aw);
16024             }
16025             this.onResize(aw, ah, w, h);
16026             this.fireEvent('resize', this, aw, ah, w, h);
16027         }
16028         return this;
16029     },
16030
16031     /**
16032      * Gets the current size of the component's underlying element.
16033      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16034      */
16035     getSize : function(){
16036         return this.el.getSize();
16037     },
16038
16039     /**
16040      * Gets the current XY position of the component's underlying element.
16041      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16042      * @return {Array} The XY position of the element (e.g., [100, 200])
16043      */
16044     getPosition : function(local){
16045         if(local === true){
16046             return [this.el.getLeft(true), this.el.getTop(true)];
16047         }
16048         return this.xy || this.el.getXY();
16049     },
16050
16051     /**
16052      * Gets the current box measurements of the component's underlying element.
16053      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16054      * @returns {Object} box An object in the format {x, y, width, height}
16055      */
16056     getBox : function(local){
16057         var s = this.el.getSize();
16058         if(local){
16059             s.x = this.el.getLeft(true);
16060             s.y = this.el.getTop(true);
16061         }else{
16062             var xy = this.xy || this.el.getXY();
16063             s.x = xy[0];
16064             s.y = xy[1];
16065         }
16066         return s;
16067     },
16068
16069     /**
16070      * Sets the current box measurements of the component's underlying element.
16071      * @param {Object} box An object in the format {x, y, width, height}
16072      * @returns {Roo.BoxComponent} this
16073      */
16074     updateBox : function(box){
16075         this.setSize(box.width, box.height);
16076         this.setPagePosition(box.x, box.y);
16077         return this;
16078     },
16079
16080     // protected
16081     getResizeEl : function(){
16082         return this.resizeEl || this.el;
16083     },
16084
16085     // protected
16086     getPositionEl : function(){
16087         return this.positionEl || this.el;
16088     },
16089
16090     /**
16091      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16092      * This method fires the move event.
16093      * @param {Number} left The new left
16094      * @param {Number} top The new top
16095      * @returns {Roo.BoxComponent} this
16096      */
16097     setPosition : function(x, y){
16098         this.x = x;
16099         this.y = y;
16100         if(!this.boxReady){
16101             return this;
16102         }
16103         var adj = this.adjustPosition(x, y);
16104         var ax = adj.x, ay = adj.y;
16105
16106         var el = this.getPositionEl();
16107         if(ax !== undefined || ay !== undefined){
16108             if(ax !== undefined && ay !== undefined){
16109                 el.setLeftTop(ax, ay);
16110             }else if(ax !== undefined){
16111                 el.setLeft(ax);
16112             }else if(ay !== undefined){
16113                 el.setTop(ay);
16114             }
16115             this.onPosition(ax, ay);
16116             this.fireEvent('move', this, ax, ay);
16117         }
16118         return this;
16119     },
16120
16121     /**
16122      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16123      * This method fires the move event.
16124      * @param {Number} x The new x position
16125      * @param {Number} y The new y position
16126      * @returns {Roo.BoxComponent} this
16127      */
16128     setPagePosition : function(x, y){
16129         this.pageX = x;
16130         this.pageY = y;
16131         if(!this.boxReady){
16132             return;
16133         }
16134         if(x === undefined || y === undefined){ // cannot translate undefined points
16135             return;
16136         }
16137         var p = this.el.translatePoints(x, y);
16138         this.setPosition(p.left, p.top);
16139         return this;
16140     },
16141
16142     // private
16143     onRender : function(ct, position){
16144         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16145         if(this.resizeEl){
16146             this.resizeEl = Roo.get(this.resizeEl);
16147         }
16148         if(this.positionEl){
16149             this.positionEl = Roo.get(this.positionEl);
16150         }
16151     },
16152
16153     // private
16154     afterRender : function(){
16155         Roo.BoxComponent.superclass.afterRender.call(this);
16156         this.boxReady = true;
16157         this.setSize(this.width, this.height);
16158         if(this.x || this.y){
16159             this.setPosition(this.x, this.y);
16160         }
16161         if(this.pageX || this.pageY){
16162             this.setPagePosition(this.pageX, this.pageY);
16163         }
16164     },
16165
16166     /**
16167      * Force the component's size to recalculate based on the underlying element's current height and width.
16168      * @returns {Roo.BoxComponent} this
16169      */
16170     syncSize : function(){
16171         delete this.lastSize;
16172         this.setSize(this.el.getWidth(), this.el.getHeight());
16173         return this;
16174     },
16175
16176     /**
16177      * Called after the component is resized, this method is empty by default but can be implemented by any
16178      * subclass that needs to perform custom logic after a resize occurs.
16179      * @param {Number} adjWidth The box-adjusted width that was set
16180      * @param {Number} adjHeight The box-adjusted height that was set
16181      * @param {Number} rawWidth The width that was originally specified
16182      * @param {Number} rawHeight The height that was originally specified
16183      */
16184     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16185
16186     },
16187
16188     /**
16189      * Called after the component is moved, this method is empty by default but can be implemented by any
16190      * subclass that needs to perform custom logic after a move occurs.
16191      * @param {Number} x The new x position
16192      * @param {Number} y The new y position
16193      */
16194     onPosition : function(x, y){
16195
16196     },
16197
16198     // private
16199     adjustSize : function(w, h){
16200         if(this.autoWidth){
16201             w = 'auto';
16202         }
16203         if(this.autoHeight){
16204             h = 'auto';
16205         }
16206         return {width : w, height: h};
16207     },
16208
16209     // private
16210     adjustPosition : function(x, y){
16211         return {x : x, y: y};
16212     }
16213 });/*
16214  * Based on:
16215  * Ext JS Library 1.1.1
16216  * Copyright(c) 2006-2007, Ext JS, LLC.
16217  *
16218  * Originally Released Under LGPL - original licence link has changed is not relivant.
16219  *
16220  * Fork - LGPL
16221  * <script type="text/javascript">
16222  */
16223  (function(){ 
16224 /**
16225  * @class Roo.Layer
16226  * @extends Roo.Element
16227  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16228  * automatic maintaining of shadow/shim positions.
16229  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16230  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16231  * you can pass a string with a CSS class name. False turns off the shadow.
16232  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16233  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16234  * @cfg {String} cls CSS class to add to the element
16235  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16236  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16237  * @constructor
16238  * @param {Object} config An object with config options.
16239  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16240  */
16241
16242 Roo.Layer = function(config, existingEl){
16243     config = config || {};
16244     var dh = Roo.DomHelper;
16245     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16246     if(existingEl){
16247         this.dom = Roo.getDom(existingEl);
16248     }
16249     if(!this.dom){
16250         var o = config.dh || {tag: "div", cls: "x-layer"};
16251         this.dom = dh.append(pel, o);
16252     }
16253     if(config.cls){
16254         this.addClass(config.cls);
16255     }
16256     this.constrain = config.constrain !== false;
16257     this.visibilityMode = Roo.Element.VISIBILITY;
16258     if(config.id){
16259         this.id = this.dom.id = config.id;
16260     }else{
16261         this.id = Roo.id(this.dom);
16262     }
16263     this.zindex = config.zindex || this.getZIndex();
16264     this.position("absolute", this.zindex);
16265     if(config.shadow){
16266         this.shadowOffset = config.shadowOffset || 4;
16267         this.shadow = new Roo.Shadow({
16268             offset : this.shadowOffset,
16269             mode : config.shadow
16270         });
16271     }else{
16272         this.shadowOffset = 0;
16273     }
16274     this.useShim = config.shim !== false && Roo.useShims;
16275     this.useDisplay = config.useDisplay;
16276     this.hide();
16277 };
16278
16279 var supr = Roo.Element.prototype;
16280
16281 // shims are shared among layer to keep from having 100 iframes
16282 var shims = [];
16283
16284 Roo.extend(Roo.Layer, Roo.Element, {
16285
16286     getZIndex : function(){
16287         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16288     },
16289
16290     getShim : function(){
16291         if(!this.useShim){
16292             return null;
16293         }
16294         if(this.shim){
16295             return this.shim;
16296         }
16297         var shim = shims.shift();
16298         if(!shim){
16299             shim = this.createShim();
16300             shim.enableDisplayMode('block');
16301             shim.dom.style.display = 'none';
16302             shim.dom.style.visibility = 'visible';
16303         }
16304         var pn = this.dom.parentNode;
16305         if(shim.dom.parentNode != pn){
16306             pn.insertBefore(shim.dom, this.dom);
16307         }
16308         shim.setStyle('z-index', this.getZIndex()-2);
16309         this.shim = shim;
16310         return shim;
16311     },
16312
16313     hideShim : function(){
16314         if(this.shim){
16315             this.shim.setDisplayed(false);
16316             shims.push(this.shim);
16317             delete this.shim;
16318         }
16319     },
16320
16321     disableShadow : function(){
16322         if(this.shadow){
16323             this.shadowDisabled = true;
16324             this.shadow.hide();
16325             this.lastShadowOffset = this.shadowOffset;
16326             this.shadowOffset = 0;
16327         }
16328     },
16329
16330     enableShadow : function(show){
16331         if(this.shadow){
16332             this.shadowDisabled = false;
16333             this.shadowOffset = this.lastShadowOffset;
16334             delete this.lastShadowOffset;
16335             if(show){
16336                 this.sync(true);
16337             }
16338         }
16339     },
16340
16341     // private
16342     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16343     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16344     sync : function(doShow){
16345         var sw = this.shadow;
16346         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16347             var sh = this.getShim();
16348
16349             var w = this.getWidth(),
16350                 h = this.getHeight();
16351
16352             var l = this.getLeft(true),
16353                 t = this.getTop(true);
16354
16355             if(sw && !this.shadowDisabled){
16356                 if(doShow && !sw.isVisible()){
16357                     sw.show(this);
16358                 }else{
16359                     sw.realign(l, t, w, h);
16360                 }
16361                 if(sh){
16362                     if(doShow){
16363                        sh.show();
16364                     }
16365                     // fit the shim behind the shadow, so it is shimmed too
16366                     var a = sw.adjusts, s = sh.dom.style;
16367                     s.left = (Math.min(l, l+a.l))+"px";
16368                     s.top = (Math.min(t, t+a.t))+"px";
16369                     s.width = (w+a.w)+"px";
16370                     s.height = (h+a.h)+"px";
16371                 }
16372             }else if(sh){
16373                 if(doShow){
16374                    sh.show();
16375                 }
16376                 sh.setSize(w, h);
16377                 sh.setLeftTop(l, t);
16378             }
16379             
16380         }
16381     },
16382
16383     // private
16384     destroy : function(){
16385         this.hideShim();
16386         if(this.shadow){
16387             this.shadow.hide();
16388         }
16389         this.removeAllListeners();
16390         var pn = this.dom.parentNode;
16391         if(pn){
16392             pn.removeChild(this.dom);
16393         }
16394         Roo.Element.uncache(this.id);
16395     },
16396
16397     remove : function(){
16398         this.destroy();
16399     },
16400
16401     // private
16402     beginUpdate : function(){
16403         this.updating = true;
16404     },
16405
16406     // private
16407     endUpdate : function(){
16408         this.updating = false;
16409         this.sync(true);
16410     },
16411
16412     // private
16413     hideUnders : function(negOffset){
16414         if(this.shadow){
16415             this.shadow.hide();
16416         }
16417         this.hideShim();
16418     },
16419
16420     // private
16421     constrainXY : function(){
16422         if(this.constrain){
16423             var vw = Roo.lib.Dom.getViewWidth(),
16424                 vh = Roo.lib.Dom.getViewHeight();
16425             var s = Roo.get(document).getScroll();
16426
16427             var xy = this.getXY();
16428             var x = xy[0], y = xy[1];   
16429             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16430             // only move it if it needs it
16431             var moved = false;
16432             // first validate right/bottom
16433             if((x + w) > vw+s.left){
16434                 x = vw - w - this.shadowOffset;
16435                 moved = true;
16436             }
16437             if((y + h) > vh+s.top){
16438                 y = vh - h - this.shadowOffset;
16439                 moved = true;
16440             }
16441             // then make sure top/left isn't negative
16442             if(x < s.left){
16443                 x = s.left;
16444                 moved = true;
16445             }
16446             if(y < s.top){
16447                 y = s.top;
16448                 moved = true;
16449             }
16450             if(moved){
16451                 if(this.avoidY){
16452                     var ay = this.avoidY;
16453                     if(y <= ay && (y+h) >= ay){
16454                         y = ay-h-5;   
16455                     }
16456                 }
16457                 xy = [x, y];
16458                 this.storeXY(xy);
16459                 supr.setXY.call(this, xy);
16460                 this.sync();
16461             }
16462         }
16463     },
16464
16465     isVisible : function(){
16466         return this.visible;    
16467     },
16468
16469     // private
16470     showAction : function(){
16471         this.visible = true; // track visibility to prevent getStyle calls
16472         if(this.useDisplay === true){
16473             this.setDisplayed("");
16474         }else if(this.lastXY){
16475             supr.setXY.call(this, this.lastXY);
16476         }else if(this.lastLT){
16477             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16478         }
16479     },
16480
16481     // private
16482     hideAction : function(){
16483         this.visible = false;
16484         if(this.useDisplay === true){
16485             this.setDisplayed(false);
16486         }else{
16487             this.setLeftTop(-10000,-10000);
16488         }
16489     },
16490
16491     // overridden Element method
16492     setVisible : function(v, a, d, c, e){
16493         if(v){
16494             this.showAction();
16495         }
16496         if(a && v){
16497             var cb = function(){
16498                 this.sync(true);
16499                 if(c){
16500                     c();
16501                 }
16502             }.createDelegate(this);
16503             supr.setVisible.call(this, true, true, d, cb, e);
16504         }else{
16505             if(!v){
16506                 this.hideUnders(true);
16507             }
16508             var cb = c;
16509             if(a){
16510                 cb = function(){
16511                     this.hideAction();
16512                     if(c){
16513                         c();
16514                     }
16515                 }.createDelegate(this);
16516             }
16517             supr.setVisible.call(this, v, a, d, cb, e);
16518             if(v){
16519                 this.sync(true);
16520             }else if(!a){
16521                 this.hideAction();
16522             }
16523         }
16524     },
16525
16526     storeXY : function(xy){
16527         delete this.lastLT;
16528         this.lastXY = xy;
16529     },
16530
16531     storeLeftTop : function(left, top){
16532         delete this.lastXY;
16533         this.lastLT = [left, top];
16534     },
16535
16536     // private
16537     beforeFx : function(){
16538         this.beforeAction();
16539         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16540     },
16541
16542     // private
16543     afterFx : function(){
16544         Roo.Layer.superclass.afterFx.apply(this, arguments);
16545         this.sync(this.isVisible());
16546     },
16547
16548     // private
16549     beforeAction : function(){
16550         if(!this.updating && this.shadow){
16551             this.shadow.hide();
16552         }
16553     },
16554
16555     // overridden Element method
16556     setLeft : function(left){
16557         this.storeLeftTop(left, this.getTop(true));
16558         supr.setLeft.apply(this, arguments);
16559         this.sync();
16560     },
16561
16562     setTop : function(top){
16563         this.storeLeftTop(this.getLeft(true), top);
16564         supr.setTop.apply(this, arguments);
16565         this.sync();
16566     },
16567
16568     setLeftTop : function(left, top){
16569         this.storeLeftTop(left, top);
16570         supr.setLeftTop.apply(this, arguments);
16571         this.sync();
16572     },
16573
16574     setXY : function(xy, a, d, c, e){
16575         this.fixDisplay();
16576         this.beforeAction();
16577         this.storeXY(xy);
16578         var cb = this.createCB(c);
16579         supr.setXY.call(this, xy, a, d, cb, e);
16580         if(!a){
16581             cb();
16582         }
16583     },
16584
16585     // private
16586     createCB : function(c){
16587         var el = this;
16588         return function(){
16589             el.constrainXY();
16590             el.sync(true);
16591             if(c){
16592                 c();
16593             }
16594         };
16595     },
16596
16597     // overridden Element method
16598     setX : function(x, a, d, c, e){
16599         this.setXY([x, this.getY()], a, d, c, e);
16600     },
16601
16602     // overridden Element method
16603     setY : function(y, a, d, c, e){
16604         this.setXY([this.getX(), y], a, d, c, e);
16605     },
16606
16607     // overridden Element method
16608     setSize : function(w, h, a, d, c, e){
16609         this.beforeAction();
16610         var cb = this.createCB(c);
16611         supr.setSize.call(this, w, h, a, d, cb, e);
16612         if(!a){
16613             cb();
16614         }
16615     },
16616
16617     // overridden Element method
16618     setWidth : function(w, a, d, c, e){
16619         this.beforeAction();
16620         var cb = this.createCB(c);
16621         supr.setWidth.call(this, w, a, d, cb, e);
16622         if(!a){
16623             cb();
16624         }
16625     },
16626
16627     // overridden Element method
16628     setHeight : function(h, a, d, c, e){
16629         this.beforeAction();
16630         var cb = this.createCB(c);
16631         supr.setHeight.call(this, h, a, d, cb, e);
16632         if(!a){
16633             cb();
16634         }
16635     },
16636
16637     // overridden Element method
16638     setBounds : function(x, y, w, h, a, d, c, e){
16639         this.beforeAction();
16640         var cb = this.createCB(c);
16641         if(!a){
16642             this.storeXY([x, y]);
16643             supr.setXY.call(this, [x, y]);
16644             supr.setSize.call(this, w, h, a, d, cb, e);
16645             cb();
16646         }else{
16647             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16648         }
16649         return this;
16650     },
16651     
16652     /**
16653      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16654      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16655      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16656      * @param {Number} zindex The new z-index to set
16657      * @return {this} The Layer
16658      */
16659     setZIndex : function(zindex){
16660         this.zindex = zindex;
16661         this.setStyle("z-index", zindex + 2);
16662         if(this.shadow){
16663             this.shadow.setZIndex(zindex + 1);
16664         }
16665         if(this.shim){
16666             this.shim.setStyle("z-index", zindex);
16667         }
16668     }
16669 });
16670 })();/*
16671  * Original code for Roojs - LGPL
16672  * <script type="text/javascript">
16673  */
16674  
16675 /**
16676  * @class Roo.XComponent
16677  * A delayed Element creator...
16678  * Or a way to group chunks of interface together.
16679  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16680  *  used in conjunction with XComponent.build() it will create an instance of each element,
16681  *  then call addxtype() to build the User interface.
16682  * 
16683  * Mypart.xyx = new Roo.XComponent({
16684
16685     parent : 'Mypart.xyz', // empty == document.element.!!
16686     order : '001',
16687     name : 'xxxx'
16688     region : 'xxxx'
16689     disabled : function() {} 
16690      
16691     tree : function() { // return an tree of xtype declared components
16692         var MODULE = this;
16693         return 
16694         {
16695             xtype : 'NestedLayoutPanel',
16696             // technicall
16697         }
16698      ]
16699  *})
16700  *
16701  *
16702  * It can be used to build a big heiracy, with parent etc.
16703  * or you can just use this to render a single compoent to a dom element
16704  * MYPART.render(Roo.Element | String(id) | dom_element )
16705  *
16706  *
16707  * Usage patterns.
16708  *
16709  * Classic Roo
16710  *
16711  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16712  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16713  *
16714  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16715  *
16716  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16717  * - if mulitple topModules exist, the last one is defined as the top module.
16718  *
16719  * Embeded Roo
16720  * 
16721  * When the top level or multiple modules are to embedded into a existing HTML page,
16722  * the parent element can container '#id' of the element where the module will be drawn.
16723  *
16724  * Bootstrap Roo
16725  *
16726  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16727  * it relies more on a include mechanism, where sub modules are included into an outer page.
16728  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16729  * 
16730  * Bootstrap Roo Included elements
16731  *
16732  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16733  * hence confusing the component builder as it thinks there are multiple top level elements. 
16734  *
16735  * String Over-ride & Translations
16736  *
16737  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16738  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16739  * are needed. @see Roo.XComponent.overlayString  
16740  * 
16741  * 
16742  * 
16743  * @extends Roo.util.Observable
16744  * @constructor
16745  * @param cfg {Object} configuration of component
16746  * 
16747  */
16748 Roo.XComponent = function(cfg) {
16749     Roo.apply(this, cfg);
16750     this.addEvents({ 
16751         /**
16752              * @event built
16753              * Fires when this the componnt is built
16754              * @param {Roo.XComponent} c the component
16755              */
16756         'built' : true
16757         
16758     });
16759     this.region = this.region || 'center'; // default..
16760     Roo.XComponent.register(this);
16761     this.modules = false;
16762     this.el = false; // where the layout goes..
16763     
16764     
16765 }
16766 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16767     /**
16768      * @property el
16769      * The created element (with Roo.factory())
16770      * @type {Roo.Layout}
16771      */
16772     el  : false,
16773     
16774     /**
16775      * @property el
16776      * for BC  - use el in new code
16777      * @type {Roo.Layout}
16778      */
16779     panel : false,
16780     
16781     /**
16782      * @property layout
16783      * for BC  - use el in new code
16784      * @type {Roo.Layout}
16785      */
16786     layout : false,
16787     
16788      /**
16789      * @cfg {Function|boolean} disabled
16790      * If this module is disabled by some rule, return true from the funtion
16791      */
16792     disabled : false,
16793     
16794     /**
16795      * @cfg {String} parent 
16796      * Name of parent element which it get xtype added to..
16797      */
16798     parent: false,
16799     
16800     /**
16801      * @cfg {String} order
16802      * Used to set the order in which elements are created (usefull for multiple tabs)
16803      */
16804     
16805     order : false,
16806     /**
16807      * @cfg {String} name
16808      * String to display while loading.
16809      */
16810     name : false,
16811     /**
16812      * @cfg {String} region
16813      * Region to render component to (defaults to center)
16814      */
16815     region : 'center',
16816     
16817     /**
16818      * @cfg {Array} items
16819      * A single item array - the first element is the root of the tree..
16820      * It's done this way to stay compatible with the Xtype system...
16821      */
16822     items : false,
16823     
16824     /**
16825      * @property _tree
16826      * The method that retuns the tree of parts that make up this compoennt 
16827      * @type {function}
16828      */
16829     _tree  : false,
16830     
16831      /**
16832      * render
16833      * render element to dom or tree
16834      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16835      */
16836     
16837     render : function(el)
16838     {
16839         
16840         el = el || false;
16841         var hp = this.parent ? 1 : 0;
16842         Roo.debug &&  Roo.log(this);
16843         
16844         var tree = this._tree ? this._tree() : this.tree();
16845
16846         
16847         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16848             // if parent is a '#.....' string, then let's use that..
16849             var ename = this.parent.substr(1);
16850             this.parent = false;
16851             Roo.debug && Roo.log(ename);
16852             switch (ename) {
16853                 case 'bootstrap-body':
16854                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16855                         // this is the BorderLayout standard?
16856                        this.parent = { el : true };
16857                        break;
16858                     }
16859                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16860                         // need to insert stuff...
16861                         this.parent =  {
16862                              el : new Roo.bootstrap.layout.Border({
16863                                  el : document.body, 
16864                      
16865                                  center: {
16866                                     titlebar: false,
16867                                     autoScroll:false,
16868                                     closeOnTab: true,
16869                                     tabPosition: 'top',
16870                                       //resizeTabs: true,
16871                                     alwaysShowTabs: true,
16872                                     hideTabs: false
16873                                      //minTabWidth: 140
16874                                  }
16875                              })
16876                         
16877                          };
16878                          break;
16879                     }
16880                          
16881                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16882                         this.parent = { el :  new  Roo.bootstrap.Body() };
16883                         Roo.debug && Roo.log("setting el to doc body");
16884                          
16885                     } else {
16886                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16887                     }
16888                     break;
16889                 case 'bootstrap':
16890                     this.parent = { el : true};
16891                     // fall through
16892                 default:
16893                     el = Roo.get(ename);
16894                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16895                         this.parent = { el : true};
16896                     }
16897                     
16898                     break;
16899             }
16900                 
16901             
16902             if (!el && !this.parent) {
16903                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16904                 return;
16905             }
16906         }
16907         
16908         Roo.debug && Roo.log("EL:");
16909         Roo.debug && Roo.log(el);
16910         Roo.debug && Roo.log("this.parent.el:");
16911         Roo.debug && Roo.log(this.parent.el);
16912         
16913
16914         // altertive root elements ??? - we need a better way to indicate these.
16915         var is_alt = Roo.XComponent.is_alt ||
16916                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16917                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16918                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16919         
16920         
16921         
16922         if (!this.parent && is_alt) {
16923             //el = Roo.get(document.body);
16924             this.parent = { el : true };
16925         }
16926             
16927             
16928         
16929         if (!this.parent) {
16930             
16931             Roo.debug && Roo.log("no parent - creating one");
16932             
16933             el = el ? Roo.get(el) : false;      
16934             
16935             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16936                 
16937                 this.parent =  {
16938                     el : new Roo.bootstrap.layout.Border({
16939                         el: el || document.body,
16940                     
16941                         center: {
16942                             titlebar: false,
16943                             autoScroll:false,
16944                             closeOnTab: true,
16945                             tabPosition: 'top',
16946                              //resizeTabs: true,
16947                             alwaysShowTabs: false,
16948                             hideTabs: true,
16949                             minTabWidth: 140,
16950                             overflow: 'visible'
16951                          }
16952                      })
16953                 };
16954             } else {
16955             
16956                 // it's a top level one..
16957                 this.parent =  {
16958                     el : new Roo.BorderLayout(el || document.body, {
16959                         center: {
16960                             titlebar: false,
16961                             autoScroll:false,
16962                             closeOnTab: true,
16963                             tabPosition: 'top',
16964                              //resizeTabs: true,
16965                             alwaysShowTabs: el && hp? false :  true,
16966                             hideTabs: el || !hp ? true :  false,
16967                             minTabWidth: 140
16968                          }
16969                     })
16970                 };
16971             }
16972         }
16973         
16974         if (!this.parent.el) {
16975                 // probably an old style ctor, which has been disabled.
16976                 return;
16977
16978         }
16979                 // The 'tree' method is  '_tree now' 
16980             
16981         tree.region = tree.region || this.region;
16982         var is_body = false;
16983         if (this.parent.el === true) {
16984             // bootstrap... - body..
16985             if (el) {
16986                 tree.el = el;
16987             }
16988             this.parent.el = Roo.factory(tree);
16989             is_body = true;
16990         }
16991         
16992         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16993         this.fireEvent('built', this);
16994         
16995         this.panel = this.el;
16996         this.layout = this.panel.layout;
16997         this.parentLayout = this.parent.layout  || false;  
16998          
16999     }
17000     
17001 });
17002
17003 Roo.apply(Roo.XComponent, {
17004     /**
17005      * @property  hideProgress
17006      * true to disable the building progress bar.. usefull on single page renders.
17007      * @type Boolean
17008      */
17009     hideProgress : false,
17010     /**
17011      * @property  buildCompleted
17012      * True when the builder has completed building the interface.
17013      * @type Boolean
17014      */
17015     buildCompleted : false,
17016      
17017     /**
17018      * @property  topModule
17019      * the upper most module - uses document.element as it's constructor.
17020      * @type Object
17021      */
17022      
17023     topModule  : false,
17024       
17025     /**
17026      * @property  modules
17027      * array of modules to be created by registration system.
17028      * @type {Array} of Roo.XComponent
17029      */
17030     
17031     modules : [],
17032     /**
17033      * @property  elmodules
17034      * array of modules to be created by which use #ID 
17035      * @type {Array} of Roo.XComponent
17036      */
17037      
17038     elmodules : [],
17039
17040      /**
17041      * @property  is_alt
17042      * Is an alternative Root - normally used by bootstrap or other systems,
17043      *    where the top element in the tree can wrap 'body' 
17044      * @type {boolean}  (default false)
17045      */
17046      
17047     is_alt : false,
17048     /**
17049      * @property  build_from_html
17050      * Build elements from html - used by bootstrap HTML stuff 
17051      *    - this is cleared after build is completed
17052      * @type {boolean}    (default false)
17053      */
17054      
17055     build_from_html : false,
17056     /**
17057      * Register components to be built later.
17058      *
17059      * This solves the following issues
17060      * - Building is not done on page load, but after an authentication process has occured.
17061      * - Interface elements are registered on page load
17062      * - Parent Interface elements may not be loaded before child, so this handles that..
17063      * 
17064      *
17065      * example:
17066      * 
17067      * MyApp.register({
17068           order : '000001',
17069           module : 'Pman.Tab.projectMgr',
17070           region : 'center',
17071           parent : 'Pman.layout',
17072           disabled : false,  // or use a function..
17073         })
17074      
17075      * * @param {Object} details about module
17076      */
17077     register : function(obj) {
17078                 
17079         Roo.XComponent.event.fireEvent('register', obj);
17080         switch(typeof(obj.disabled) ) {
17081                 
17082             case 'undefined':
17083                 break;
17084             
17085             case 'function':
17086                 if ( obj.disabled() ) {
17087                         return;
17088                 }
17089                 break;
17090             
17091             default:
17092                 if (obj.disabled || obj.region == '#disabled') {
17093                         return;
17094                 }
17095                 break;
17096         }
17097                 
17098         this.modules.push(obj);
17099          
17100     },
17101     /**
17102      * convert a string to an object..
17103      * eg. 'AAA.BBB' -> finds AAA.BBB
17104
17105      */
17106     
17107     toObject : function(str)
17108     {
17109         if (!str || typeof(str) == 'object') {
17110             return str;
17111         }
17112         if (str.substring(0,1) == '#') {
17113             return str;
17114         }
17115
17116         var ar = str.split('.');
17117         var rt, o;
17118         rt = ar.shift();
17119             /** eval:var:o */
17120         try {
17121             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17122         } catch (e) {
17123             throw "Module not found : " + str;
17124         }
17125         
17126         if (o === false) {
17127             throw "Module not found : " + str;
17128         }
17129         Roo.each(ar, function(e) {
17130             if (typeof(o[e]) == 'undefined') {
17131                 throw "Module not found : " + str;
17132             }
17133             o = o[e];
17134         });
17135         
17136         return o;
17137         
17138     },
17139     
17140     
17141     /**
17142      * move modules into their correct place in the tree..
17143      * 
17144      */
17145     preBuild : function ()
17146     {
17147         var _t = this;
17148         Roo.each(this.modules , function (obj)
17149         {
17150             Roo.XComponent.event.fireEvent('beforebuild', obj);
17151             
17152             var opar = obj.parent;
17153             try { 
17154                 obj.parent = this.toObject(opar);
17155             } catch(e) {
17156                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17157                 return;
17158             }
17159             
17160             if (!obj.parent) {
17161                 Roo.debug && Roo.log("GOT top level module");
17162                 Roo.debug && Roo.log(obj);
17163                 obj.modules = new Roo.util.MixedCollection(false, 
17164                     function(o) { return o.order + '' }
17165                 );
17166                 this.topModule = obj;
17167                 return;
17168             }
17169                         // parent is a string (usually a dom element name..)
17170             if (typeof(obj.parent) == 'string') {
17171                 this.elmodules.push(obj);
17172                 return;
17173             }
17174             if (obj.parent.constructor != Roo.XComponent) {
17175                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17176             }
17177             if (!obj.parent.modules) {
17178                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17179                     function(o) { return o.order + '' }
17180                 );
17181             }
17182             if (obj.parent.disabled) {
17183                 obj.disabled = true;
17184             }
17185             obj.parent.modules.add(obj);
17186         }, this);
17187     },
17188     
17189      /**
17190      * make a list of modules to build.
17191      * @return {Array} list of modules. 
17192      */ 
17193     
17194     buildOrder : function()
17195     {
17196         var _this = this;
17197         var cmp = function(a,b) {   
17198             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17199         };
17200         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17201             throw "No top level modules to build";
17202         }
17203         
17204         // make a flat list in order of modules to build.
17205         var mods = this.topModule ? [ this.topModule ] : [];
17206                 
17207         
17208         // elmodules (is a list of DOM based modules )
17209         Roo.each(this.elmodules, function(e) {
17210             mods.push(e);
17211             if (!this.topModule &&
17212                 typeof(e.parent) == 'string' &&
17213                 e.parent.substring(0,1) == '#' &&
17214                 Roo.get(e.parent.substr(1))
17215                ) {
17216                 
17217                 _this.topModule = e;
17218             }
17219             
17220         });
17221
17222         
17223         // add modules to their parents..
17224         var addMod = function(m) {
17225             Roo.debug && Roo.log("build Order: add: " + m.name);
17226                 
17227             mods.push(m);
17228             if (m.modules && !m.disabled) {
17229                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17230                 m.modules.keySort('ASC',  cmp );
17231                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17232     
17233                 m.modules.each(addMod);
17234             } else {
17235                 Roo.debug && Roo.log("build Order: no child modules");
17236             }
17237             // not sure if this is used any more..
17238             if (m.finalize) {
17239                 m.finalize.name = m.name + " (clean up) ";
17240                 mods.push(m.finalize);
17241             }
17242             
17243         }
17244         if (this.topModule && this.topModule.modules) { 
17245             this.topModule.modules.keySort('ASC',  cmp );
17246             this.topModule.modules.each(addMod);
17247         } 
17248         return mods;
17249     },
17250     
17251      /**
17252      * Build the registered modules.
17253      * @param {Object} parent element.
17254      * @param {Function} optional method to call after module has been added.
17255      * 
17256      */ 
17257    
17258     build : function(opts) 
17259     {
17260         
17261         if (typeof(opts) != 'undefined') {
17262             Roo.apply(this,opts);
17263         }
17264         
17265         this.preBuild();
17266         var mods = this.buildOrder();
17267       
17268         //this.allmods = mods;
17269         //Roo.debug && Roo.log(mods);
17270         //return;
17271         if (!mods.length) { // should not happen
17272             throw "NO modules!!!";
17273         }
17274         
17275         
17276         var msg = "Building Interface...";
17277         // flash it up as modal - so we store the mask!?
17278         if (!this.hideProgress && Roo.MessageBox) {
17279             Roo.MessageBox.show({ title: 'loading' });
17280             Roo.MessageBox.show({
17281                title: "Please wait...",
17282                msg: msg,
17283                width:450,
17284                progress:true,
17285                buttons : false,
17286                closable:false,
17287                modal: false
17288               
17289             });
17290         }
17291         var total = mods.length;
17292         
17293         var _this = this;
17294         var progressRun = function() {
17295             if (!mods.length) {
17296                 Roo.debug && Roo.log('hide?');
17297                 if (!this.hideProgress && Roo.MessageBox) {
17298                     Roo.MessageBox.hide();
17299                 }
17300                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17301                 
17302                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17303                 
17304                 // THE END...
17305                 return false;   
17306             }
17307             
17308             var m = mods.shift();
17309             
17310             
17311             Roo.debug && Roo.log(m);
17312             // not sure if this is supported any more.. - modules that are are just function
17313             if (typeof(m) == 'function') { 
17314                 m.call(this);
17315                 return progressRun.defer(10, _this);
17316             } 
17317             
17318             
17319             msg = "Building Interface " + (total  - mods.length) + 
17320                     " of " + total + 
17321                     (m.name ? (' - ' + m.name) : '');
17322                         Roo.debug && Roo.log(msg);
17323             if (!_this.hideProgress &&  Roo.MessageBox) { 
17324                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17325             }
17326             
17327          
17328             // is the module disabled?
17329             var disabled = (typeof(m.disabled) == 'function') ?
17330                 m.disabled.call(m.module.disabled) : m.disabled;    
17331             
17332             
17333             if (disabled) {
17334                 return progressRun(); // we do not update the display!
17335             }
17336             
17337             // now build 
17338             
17339                         
17340                         
17341             m.render();
17342             // it's 10 on top level, and 1 on others??? why...
17343             return progressRun.defer(10, _this);
17344              
17345         }
17346         progressRun.defer(1, _this);
17347      
17348         
17349         
17350     },
17351     /**
17352      * Overlay a set of modified strings onto a component
17353      * This is dependant on our builder exporting the strings and 'named strings' elements.
17354      * 
17355      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17356      * @param {Object} associative array of 'named' string and it's new value.
17357      * 
17358      */
17359         overlayStrings : function( component, strings )
17360     {
17361         if (typeof(component['_named_strings']) == 'undefined') {
17362             throw "ERROR: component does not have _named_strings";
17363         }
17364         for ( var k in strings ) {
17365             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17366             if (md !== false) {
17367                 component['_strings'][md] = strings[k];
17368             } else {
17369                 Roo.log('could not find named string: ' + k + ' in');
17370                 Roo.log(component);
17371             }
17372             
17373         }
17374         
17375     },
17376     
17377         
17378         /**
17379          * Event Object.
17380          *
17381          *
17382          */
17383         event: false, 
17384     /**
17385          * wrapper for event.on - aliased later..  
17386          * Typically use to register a event handler for register:
17387          *
17388          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17389          *
17390          */
17391     on : false
17392    
17393     
17394     
17395 });
17396
17397 Roo.XComponent.event = new Roo.util.Observable({
17398                 events : { 
17399                         /**
17400                          * @event register
17401                          * Fires when an Component is registered,
17402                          * set the disable property on the Component to stop registration.
17403                          * @param {Roo.XComponent} c the component being registerd.
17404                          * 
17405                          */
17406                         'register' : true,
17407             /**
17408                          * @event beforebuild
17409                          * Fires before each Component is built
17410                          * can be used to apply permissions.
17411                          * @param {Roo.XComponent} c the component being registerd.
17412                          * 
17413                          */
17414                         'beforebuild' : true,
17415                         /**
17416                          * @event buildcomplete
17417                          * Fires on the top level element when all elements have been built
17418                          * @param {Roo.XComponent} the top level component.
17419                          */
17420                         'buildcomplete' : true
17421                         
17422                 }
17423 });
17424
17425 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17426  //
17427  /**
17428  * marked - a markdown parser
17429  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17430  * https://github.com/chjj/marked
17431  */
17432
17433
17434 /**
17435  *
17436  * Roo.Markdown - is a very crude wrapper around marked..
17437  *
17438  * usage:
17439  * 
17440  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17441  * 
17442  * Note: move the sample code to the bottom of this
17443  * file before uncommenting it.
17444  *
17445  */
17446
17447 Roo.Markdown = {};
17448 Roo.Markdown.toHtml = function(text) {
17449     
17450     var c = new Roo.Markdown.marked.setOptions({
17451             renderer: new Roo.Markdown.marked.Renderer(),
17452             gfm: true,
17453             tables: true,
17454             breaks: false,
17455             pedantic: false,
17456             sanitize: false,
17457             smartLists: true,
17458             smartypants: false
17459           });
17460     // A FEW HACKS!!?
17461     
17462     text = text.replace(/\\\n/g,' ');
17463     return Roo.Markdown.marked(text);
17464 };
17465 //
17466 // converter
17467 //
17468 // Wraps all "globals" so that the only thing
17469 // exposed is makeHtml().
17470 //
17471 (function() {
17472     
17473      /**
17474          * eval:var:escape
17475          * eval:var:unescape
17476          * eval:var:replace
17477          */
17478       
17479     /**
17480      * Helpers
17481      */
17482     
17483     var escape = function (html, encode) {
17484       return html
17485         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17486         .replace(/</g, '&lt;')
17487         .replace(/>/g, '&gt;')
17488         .replace(/"/g, '&quot;')
17489         .replace(/'/g, '&#39;');
17490     }
17491     
17492     var unescape = function (html) {
17493         // explicitly match decimal, hex, and named HTML entities 
17494       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17495         n = n.toLowerCase();
17496         if (n === 'colon') { return ':'; }
17497         if (n.charAt(0) === '#') {
17498           return n.charAt(1) === 'x'
17499             ? String.fromCharCode(parseInt(n.substring(2), 16))
17500             : String.fromCharCode(+n.substring(1));
17501         }
17502         return '';
17503       });
17504     }
17505     
17506     var replace = function (regex, opt) {
17507       regex = regex.source;
17508       opt = opt || '';
17509       return function self(name, val) {
17510         if (!name) { return new RegExp(regex, opt); }
17511         val = val.source || val;
17512         val = val.replace(/(^|[^\[])\^/g, '$1');
17513         regex = regex.replace(name, val);
17514         return self;
17515       };
17516     }
17517
17518
17519          /**
17520          * eval:var:noop
17521     */
17522     var noop = function () {}
17523     noop.exec = noop;
17524     
17525          /**
17526          * eval:var:merge
17527     */
17528     var merge = function (obj) {
17529       var i = 1
17530         , target
17531         , key;
17532     
17533       for (; i < arguments.length; i++) {
17534         target = arguments[i];
17535         for (key in target) {
17536           if (Object.prototype.hasOwnProperty.call(target, key)) {
17537             obj[key] = target[key];
17538           }
17539         }
17540       }
17541     
17542       return obj;
17543     }
17544     
17545     
17546     /**
17547      * Block-Level Grammar
17548      */
17549     
17550     
17551     
17552     
17553     var block = {
17554       newline: /^\n+/,
17555       code: /^( {4}[^\n]+\n*)+/,
17556       fences: noop,
17557       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17558       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17559       nptable: noop,
17560       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17561       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17562       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17563       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17564       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17565       table: noop,
17566       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17567       text: /^[^\n]+/
17568     };
17569     
17570     block.bullet = /(?:[*+-]|\d+\.)/;
17571     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17572     block.item = replace(block.item, 'gm')
17573       (/bull/g, block.bullet)
17574       ();
17575     
17576     block.list = replace(block.list)
17577       (/bull/g, block.bullet)
17578       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17579       ('def', '\\n+(?=' + block.def.source + ')')
17580       ();
17581     
17582     block.blockquote = replace(block.blockquote)
17583       ('def', block.def)
17584       ();
17585     
17586     block._tag = '(?!(?:'
17587       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17588       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17589       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17590     
17591     block.html = replace(block.html)
17592       ('comment', /<!--[\s\S]*?-->/)
17593       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17594       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17595       (/tag/g, block._tag)
17596       ();
17597     
17598     block.paragraph = replace(block.paragraph)
17599       ('hr', block.hr)
17600       ('heading', block.heading)
17601       ('lheading', block.lheading)
17602       ('blockquote', block.blockquote)
17603       ('tag', '<' + block._tag)
17604       ('def', block.def)
17605       ();
17606     
17607     /**
17608      * Normal Block Grammar
17609      */
17610     
17611     block.normal = merge({}, block);
17612     
17613     /**
17614      * GFM Block Grammar
17615      */
17616     
17617     block.gfm = merge({}, block.normal, {
17618       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17619       paragraph: /^/,
17620       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17621     });
17622     
17623     block.gfm.paragraph = replace(block.paragraph)
17624       ('(?!', '(?!'
17625         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17626         + block.list.source.replace('\\1', '\\3') + '|')
17627       ();
17628     
17629     /**
17630      * GFM + Tables Block Grammar
17631      */
17632     
17633     block.tables = merge({}, block.gfm, {
17634       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17635       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17636     });
17637     
17638     /**
17639      * Block Lexer
17640      */
17641     
17642     var Lexer = function (options) {
17643       this.tokens = [];
17644       this.tokens.links = {};
17645       this.options = options || marked.defaults;
17646       this.rules = block.normal;
17647     
17648       if (this.options.gfm) {
17649         if (this.options.tables) {
17650           this.rules = block.tables;
17651         } else {
17652           this.rules = block.gfm;
17653         }
17654       }
17655     }
17656     
17657     /**
17658      * Expose Block Rules
17659      */
17660     
17661     Lexer.rules = block;
17662     
17663     /**
17664      * Static Lex Method
17665      */
17666     
17667     Lexer.lex = function(src, options) {
17668       var lexer = new Lexer(options);
17669       return lexer.lex(src);
17670     };
17671     
17672     /**
17673      * Preprocessing
17674      */
17675     
17676     Lexer.prototype.lex = function(src) {
17677       src = src
17678         .replace(/\r\n|\r/g, '\n')
17679         .replace(/\t/g, '    ')
17680         .replace(/\u00a0/g, ' ')
17681         .replace(/\u2424/g, '\n');
17682     
17683       return this.token(src, true);
17684     };
17685     
17686     /**
17687      * Lexing
17688      */
17689     
17690     Lexer.prototype.token = function(src, top, bq) {
17691       var src = src.replace(/^ +$/gm, '')
17692         , next
17693         , loose
17694         , cap
17695         , bull
17696         , b
17697         , item
17698         , space
17699         , i
17700         , l;
17701     
17702       while (src) {
17703         // newline
17704         if (cap = this.rules.newline.exec(src)) {
17705           src = src.substring(cap[0].length);
17706           if (cap[0].length > 1) {
17707             this.tokens.push({
17708               type: 'space'
17709             });
17710           }
17711         }
17712     
17713         // code
17714         if (cap = this.rules.code.exec(src)) {
17715           src = src.substring(cap[0].length);
17716           cap = cap[0].replace(/^ {4}/gm, '');
17717           this.tokens.push({
17718             type: 'code',
17719             text: !this.options.pedantic
17720               ? cap.replace(/\n+$/, '')
17721               : cap
17722           });
17723           continue;
17724         }
17725     
17726         // fences (gfm)
17727         if (cap = this.rules.fences.exec(src)) {
17728           src = src.substring(cap[0].length);
17729           this.tokens.push({
17730             type: 'code',
17731             lang: cap[2],
17732             text: cap[3] || ''
17733           });
17734           continue;
17735         }
17736     
17737         // heading
17738         if (cap = this.rules.heading.exec(src)) {
17739           src = src.substring(cap[0].length);
17740           this.tokens.push({
17741             type: 'heading',
17742             depth: cap[1].length,
17743             text: cap[2]
17744           });
17745           continue;
17746         }
17747     
17748         // table no leading pipe (gfm)
17749         if (top && (cap = this.rules.nptable.exec(src))) {
17750           src = src.substring(cap[0].length);
17751     
17752           item = {
17753             type: 'table',
17754             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17755             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17756             cells: cap[3].replace(/\n$/, '').split('\n')
17757           };
17758     
17759           for (i = 0; i < item.align.length; i++) {
17760             if (/^ *-+: *$/.test(item.align[i])) {
17761               item.align[i] = 'right';
17762             } else if (/^ *:-+: *$/.test(item.align[i])) {
17763               item.align[i] = 'center';
17764             } else if (/^ *:-+ *$/.test(item.align[i])) {
17765               item.align[i] = 'left';
17766             } else {
17767               item.align[i] = null;
17768             }
17769           }
17770     
17771           for (i = 0; i < item.cells.length; i++) {
17772             item.cells[i] = item.cells[i].split(/ *\| */);
17773           }
17774     
17775           this.tokens.push(item);
17776     
17777           continue;
17778         }
17779     
17780         // lheading
17781         if (cap = this.rules.lheading.exec(src)) {
17782           src = src.substring(cap[0].length);
17783           this.tokens.push({
17784             type: 'heading',
17785             depth: cap[2] === '=' ? 1 : 2,
17786             text: cap[1]
17787           });
17788           continue;
17789         }
17790     
17791         // hr
17792         if (cap = this.rules.hr.exec(src)) {
17793           src = src.substring(cap[0].length);
17794           this.tokens.push({
17795             type: 'hr'
17796           });
17797           continue;
17798         }
17799     
17800         // blockquote
17801         if (cap = this.rules.blockquote.exec(src)) {
17802           src = src.substring(cap[0].length);
17803     
17804           this.tokens.push({
17805             type: 'blockquote_start'
17806           });
17807     
17808           cap = cap[0].replace(/^ *> ?/gm, '');
17809     
17810           // Pass `top` to keep the current
17811           // "toplevel" state. This is exactly
17812           // how markdown.pl works.
17813           this.token(cap, top, true);
17814     
17815           this.tokens.push({
17816             type: 'blockquote_end'
17817           });
17818     
17819           continue;
17820         }
17821     
17822         // list
17823         if (cap = this.rules.list.exec(src)) {
17824           src = src.substring(cap[0].length);
17825           bull = cap[2];
17826     
17827           this.tokens.push({
17828             type: 'list_start',
17829             ordered: bull.length > 1
17830           });
17831     
17832           // Get each top-level item.
17833           cap = cap[0].match(this.rules.item);
17834     
17835           next = false;
17836           l = cap.length;
17837           i = 0;
17838     
17839           for (; i < l; i++) {
17840             item = cap[i];
17841     
17842             // Remove the list item's bullet
17843             // so it is seen as the next token.
17844             space = item.length;
17845             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17846     
17847             // Outdent whatever the
17848             // list item contains. Hacky.
17849             if (~item.indexOf('\n ')) {
17850               space -= item.length;
17851               item = !this.options.pedantic
17852                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17853                 : item.replace(/^ {1,4}/gm, '');
17854             }
17855     
17856             // Determine whether the next list item belongs here.
17857             // Backpedal if it does not belong in this list.
17858             if (this.options.smartLists && i !== l - 1) {
17859               b = block.bullet.exec(cap[i + 1])[0];
17860               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17861                 src = cap.slice(i + 1).join('\n') + src;
17862                 i = l - 1;
17863               }
17864             }
17865     
17866             // Determine whether item is loose or not.
17867             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17868             // for discount behavior.
17869             loose = next || /\n\n(?!\s*$)/.test(item);
17870             if (i !== l - 1) {
17871               next = item.charAt(item.length - 1) === '\n';
17872               if (!loose) { loose = next; }
17873             }
17874     
17875             this.tokens.push({
17876               type: loose
17877                 ? 'loose_item_start'
17878                 : 'list_item_start'
17879             });
17880     
17881             // Recurse.
17882             this.token(item, false, bq);
17883     
17884             this.tokens.push({
17885               type: 'list_item_end'
17886             });
17887           }
17888     
17889           this.tokens.push({
17890             type: 'list_end'
17891           });
17892     
17893           continue;
17894         }
17895     
17896         // html
17897         if (cap = this.rules.html.exec(src)) {
17898           src = src.substring(cap[0].length);
17899           this.tokens.push({
17900             type: this.options.sanitize
17901               ? 'paragraph'
17902               : 'html',
17903             pre: !this.options.sanitizer
17904               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17905             text: cap[0]
17906           });
17907           continue;
17908         }
17909     
17910         // def
17911         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17912           src = src.substring(cap[0].length);
17913           this.tokens.links[cap[1].toLowerCase()] = {
17914             href: cap[2],
17915             title: cap[3]
17916           };
17917           continue;
17918         }
17919     
17920         // table (gfm)
17921         if (top && (cap = this.rules.table.exec(src))) {
17922           src = src.substring(cap[0].length);
17923     
17924           item = {
17925             type: 'table',
17926             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17927             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17928             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17929           };
17930     
17931           for (i = 0; i < item.align.length; i++) {
17932             if (/^ *-+: *$/.test(item.align[i])) {
17933               item.align[i] = 'right';
17934             } else if (/^ *:-+: *$/.test(item.align[i])) {
17935               item.align[i] = 'center';
17936             } else if (/^ *:-+ *$/.test(item.align[i])) {
17937               item.align[i] = 'left';
17938             } else {
17939               item.align[i] = null;
17940             }
17941           }
17942     
17943           for (i = 0; i < item.cells.length; i++) {
17944             item.cells[i] = item.cells[i]
17945               .replace(/^ *\| *| *\| *$/g, '')
17946               .split(/ *\| */);
17947           }
17948     
17949           this.tokens.push(item);
17950     
17951           continue;
17952         }
17953     
17954         // top-level paragraph
17955         if (top && (cap = this.rules.paragraph.exec(src))) {
17956           src = src.substring(cap[0].length);
17957           this.tokens.push({
17958             type: 'paragraph',
17959             text: cap[1].charAt(cap[1].length - 1) === '\n'
17960               ? cap[1].slice(0, -1)
17961               : cap[1]
17962           });
17963           continue;
17964         }
17965     
17966         // text
17967         if (cap = this.rules.text.exec(src)) {
17968           // Top-level should never reach here.
17969           src = src.substring(cap[0].length);
17970           this.tokens.push({
17971             type: 'text',
17972             text: cap[0]
17973           });
17974           continue;
17975         }
17976     
17977         if (src) {
17978           throw new
17979             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17980         }
17981       }
17982     
17983       return this.tokens;
17984     };
17985     
17986     /**
17987      * Inline-Level Grammar
17988      */
17989     
17990     var inline = {
17991       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17992       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17993       url: noop,
17994       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17995       link: /^!?\[(inside)\]\(href\)/,
17996       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17997       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17998       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17999       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18000       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18001       br: /^ {2,}\n(?!\s*$)/,
18002       del: noop,
18003       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18004     };
18005     
18006     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18007     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18008     
18009     inline.link = replace(inline.link)
18010       ('inside', inline._inside)
18011       ('href', inline._href)
18012       ();
18013     
18014     inline.reflink = replace(inline.reflink)
18015       ('inside', inline._inside)
18016       ();
18017     
18018     /**
18019      * Normal Inline Grammar
18020      */
18021     
18022     inline.normal = merge({}, inline);
18023     
18024     /**
18025      * Pedantic Inline Grammar
18026      */
18027     
18028     inline.pedantic = merge({}, inline.normal, {
18029       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18030       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18031     });
18032     
18033     /**
18034      * GFM Inline Grammar
18035      */
18036     
18037     inline.gfm = merge({}, inline.normal, {
18038       escape: replace(inline.escape)('])', '~|])')(),
18039       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18040       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18041       text: replace(inline.text)
18042         (']|', '~]|')
18043         ('|', '|https?://|')
18044         ()
18045     });
18046     
18047     /**
18048      * GFM + Line Breaks Inline Grammar
18049      */
18050     
18051     inline.breaks = merge({}, inline.gfm, {
18052       br: replace(inline.br)('{2,}', '*')(),
18053       text: replace(inline.gfm.text)('{2,}', '*')()
18054     });
18055     
18056     /**
18057      * Inline Lexer & Compiler
18058      */
18059     
18060     var InlineLexer  = function (links, options) {
18061       this.options = options || marked.defaults;
18062       this.links = links;
18063       this.rules = inline.normal;
18064       this.renderer = this.options.renderer || new Renderer;
18065       this.renderer.options = this.options;
18066     
18067       if (!this.links) {
18068         throw new
18069           Error('Tokens array requires a `links` property.');
18070       }
18071     
18072       if (this.options.gfm) {
18073         if (this.options.breaks) {
18074           this.rules = inline.breaks;
18075         } else {
18076           this.rules = inline.gfm;
18077         }
18078       } else if (this.options.pedantic) {
18079         this.rules = inline.pedantic;
18080       }
18081     }
18082     
18083     /**
18084      * Expose Inline Rules
18085      */
18086     
18087     InlineLexer.rules = inline;
18088     
18089     /**
18090      * Static Lexing/Compiling Method
18091      */
18092     
18093     InlineLexer.output = function(src, links, options) {
18094       var inline = new InlineLexer(links, options);
18095       return inline.output(src);
18096     };
18097     
18098     /**
18099      * Lexing/Compiling
18100      */
18101     
18102     InlineLexer.prototype.output = function(src) {
18103       var out = ''
18104         , link
18105         , text
18106         , href
18107         , cap;
18108     
18109       while (src) {
18110         // escape
18111         if (cap = this.rules.escape.exec(src)) {
18112           src = src.substring(cap[0].length);
18113           out += cap[1];
18114           continue;
18115         }
18116     
18117         // autolink
18118         if (cap = this.rules.autolink.exec(src)) {
18119           src = src.substring(cap[0].length);
18120           if (cap[2] === '@') {
18121             text = cap[1].charAt(6) === ':'
18122               ? this.mangle(cap[1].substring(7))
18123               : this.mangle(cap[1]);
18124             href = this.mangle('mailto:') + text;
18125           } else {
18126             text = escape(cap[1]);
18127             href = text;
18128           }
18129           out += this.renderer.link(href, null, text);
18130           continue;
18131         }
18132     
18133         // url (gfm)
18134         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18135           src = src.substring(cap[0].length);
18136           text = escape(cap[1]);
18137           href = text;
18138           out += this.renderer.link(href, null, text);
18139           continue;
18140         }
18141     
18142         // tag
18143         if (cap = this.rules.tag.exec(src)) {
18144           if (!this.inLink && /^<a /i.test(cap[0])) {
18145             this.inLink = true;
18146           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18147             this.inLink = false;
18148           }
18149           src = src.substring(cap[0].length);
18150           out += this.options.sanitize
18151             ? this.options.sanitizer
18152               ? this.options.sanitizer(cap[0])
18153               : escape(cap[0])
18154             : cap[0];
18155           continue;
18156         }
18157     
18158         // link
18159         if (cap = this.rules.link.exec(src)) {
18160           src = src.substring(cap[0].length);
18161           this.inLink = true;
18162           out += this.outputLink(cap, {
18163             href: cap[2],
18164             title: cap[3]
18165           });
18166           this.inLink = false;
18167           continue;
18168         }
18169     
18170         // reflink, nolink
18171         if ((cap = this.rules.reflink.exec(src))
18172             || (cap = this.rules.nolink.exec(src))) {
18173           src = src.substring(cap[0].length);
18174           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18175           link = this.links[link.toLowerCase()];
18176           if (!link || !link.href) {
18177             out += cap[0].charAt(0);
18178             src = cap[0].substring(1) + src;
18179             continue;
18180           }
18181           this.inLink = true;
18182           out += this.outputLink(cap, link);
18183           this.inLink = false;
18184           continue;
18185         }
18186     
18187         // strong
18188         if (cap = this.rules.strong.exec(src)) {
18189           src = src.substring(cap[0].length);
18190           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18191           continue;
18192         }
18193     
18194         // em
18195         if (cap = this.rules.em.exec(src)) {
18196           src = src.substring(cap[0].length);
18197           out += this.renderer.em(this.output(cap[2] || cap[1]));
18198           continue;
18199         }
18200     
18201         // code
18202         if (cap = this.rules.code.exec(src)) {
18203           src = src.substring(cap[0].length);
18204           out += this.renderer.codespan(escape(cap[2], true));
18205           continue;
18206         }
18207     
18208         // br
18209         if (cap = this.rules.br.exec(src)) {
18210           src = src.substring(cap[0].length);
18211           out += this.renderer.br();
18212           continue;
18213         }
18214     
18215         // del (gfm)
18216         if (cap = this.rules.del.exec(src)) {
18217           src = src.substring(cap[0].length);
18218           out += this.renderer.del(this.output(cap[1]));
18219           continue;
18220         }
18221     
18222         // text
18223         if (cap = this.rules.text.exec(src)) {
18224           src = src.substring(cap[0].length);
18225           out += this.renderer.text(escape(this.smartypants(cap[0])));
18226           continue;
18227         }
18228     
18229         if (src) {
18230           throw new
18231             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18232         }
18233       }
18234     
18235       return out;
18236     };
18237     
18238     /**
18239      * Compile Link
18240      */
18241     
18242     InlineLexer.prototype.outputLink = function(cap, link) {
18243       var href = escape(link.href)
18244         , title = link.title ? escape(link.title) : null;
18245     
18246       return cap[0].charAt(0) !== '!'
18247         ? this.renderer.link(href, title, this.output(cap[1]))
18248         : this.renderer.image(href, title, escape(cap[1]));
18249     };
18250     
18251     /**
18252      * Smartypants Transformations
18253      */
18254     
18255     InlineLexer.prototype.smartypants = function(text) {
18256       if (!this.options.smartypants)  { return text; }
18257       return text
18258         // em-dashes
18259         .replace(/---/g, '\u2014')
18260         // en-dashes
18261         .replace(/--/g, '\u2013')
18262         // opening singles
18263         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18264         // closing singles & apostrophes
18265         .replace(/'/g, '\u2019')
18266         // opening doubles
18267         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18268         // closing doubles
18269         .replace(/"/g, '\u201d')
18270         // ellipses
18271         .replace(/\.{3}/g, '\u2026');
18272     };
18273     
18274     /**
18275      * Mangle Links
18276      */
18277     
18278     InlineLexer.prototype.mangle = function(text) {
18279       if (!this.options.mangle) { return text; }
18280       var out = ''
18281         , l = text.length
18282         , i = 0
18283         , ch;
18284     
18285       for (; i < l; i++) {
18286         ch = text.charCodeAt(i);
18287         if (Math.random() > 0.5) {
18288           ch = 'x' + ch.toString(16);
18289         }
18290         out += '&#' + ch + ';';
18291       }
18292     
18293       return out;
18294     };
18295     
18296     /**
18297      * Renderer
18298      */
18299     
18300      /**
18301          * eval:var:Renderer
18302     */
18303     
18304     var Renderer   = function (options) {
18305       this.options = options || {};
18306     }
18307     
18308     Renderer.prototype.code = function(code, lang, escaped) {
18309       if (this.options.highlight) {
18310         var out = this.options.highlight(code, lang);
18311         if (out != null && out !== code) {
18312           escaped = true;
18313           code = out;
18314         }
18315       } else {
18316             // hack!!! - it's already escapeD?
18317             escaped = true;
18318       }
18319     
18320       if (!lang) {
18321         return '<pre><code>'
18322           + (escaped ? code : escape(code, true))
18323           + '\n</code></pre>';
18324       }
18325     
18326       return '<pre><code class="'
18327         + this.options.langPrefix
18328         + escape(lang, true)
18329         + '">'
18330         + (escaped ? code : escape(code, true))
18331         + '\n</code></pre>\n';
18332     };
18333     
18334     Renderer.prototype.blockquote = function(quote) {
18335       return '<blockquote>\n' + quote + '</blockquote>\n';
18336     };
18337     
18338     Renderer.prototype.html = function(html) {
18339       return html;
18340     };
18341     
18342     Renderer.prototype.heading = function(text, level, raw) {
18343       return '<h'
18344         + level
18345         + ' id="'
18346         + this.options.headerPrefix
18347         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18348         + '">'
18349         + text
18350         + '</h'
18351         + level
18352         + '>\n';
18353     };
18354     
18355     Renderer.prototype.hr = function() {
18356       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18357     };
18358     
18359     Renderer.prototype.list = function(body, ordered) {
18360       var type = ordered ? 'ol' : 'ul';
18361       return '<' + type + '>\n' + body + '</' + type + '>\n';
18362     };
18363     
18364     Renderer.prototype.listitem = function(text) {
18365       return '<li>' + text + '</li>\n';
18366     };
18367     
18368     Renderer.prototype.paragraph = function(text) {
18369       return '<p>' + text + '</p>\n';
18370     };
18371     
18372     Renderer.prototype.table = function(header, body) {
18373       return '<table class="table table-striped">\n'
18374         + '<thead>\n'
18375         + header
18376         + '</thead>\n'
18377         + '<tbody>\n'
18378         + body
18379         + '</tbody>\n'
18380         + '</table>\n';
18381     };
18382     
18383     Renderer.prototype.tablerow = function(content) {
18384       return '<tr>\n' + content + '</tr>\n';
18385     };
18386     
18387     Renderer.prototype.tablecell = function(content, flags) {
18388       var type = flags.header ? 'th' : 'td';
18389       var tag = flags.align
18390         ? '<' + type + ' style="text-align:' + flags.align + '">'
18391         : '<' + type + '>';
18392       return tag + content + '</' + type + '>\n';
18393     };
18394     
18395     // span level renderer
18396     Renderer.prototype.strong = function(text) {
18397       return '<strong>' + text + '</strong>';
18398     };
18399     
18400     Renderer.prototype.em = function(text) {
18401       return '<em>' + text + '</em>';
18402     };
18403     
18404     Renderer.prototype.codespan = function(text) {
18405       return '<code>' + text + '</code>';
18406     };
18407     
18408     Renderer.prototype.br = function() {
18409       return this.options.xhtml ? '<br/>' : '<br>';
18410     };
18411     
18412     Renderer.prototype.del = function(text) {
18413       return '<del>' + text + '</del>';
18414     };
18415     
18416     Renderer.prototype.link = function(href, title, text) {
18417       if (this.options.sanitize) {
18418         try {
18419           var prot = decodeURIComponent(unescape(href))
18420             .replace(/[^\w:]/g, '')
18421             .toLowerCase();
18422         } catch (e) {
18423           return '';
18424         }
18425         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18426           return '';
18427         }
18428       }
18429       var out = '<a href="' + href + '"';
18430       if (title) {
18431         out += ' title="' + title + '"';
18432       }
18433       out += '>' + text + '</a>';
18434       return out;
18435     };
18436     
18437     Renderer.prototype.image = function(href, title, text) {
18438       var out = '<img src="' + href + '" alt="' + text + '"';
18439       if (title) {
18440         out += ' title="' + title + '"';
18441       }
18442       out += this.options.xhtml ? '/>' : '>';
18443       return out;
18444     };
18445     
18446     Renderer.prototype.text = function(text) {
18447       return text;
18448     };
18449     
18450     /**
18451      * Parsing & Compiling
18452      */
18453          /**
18454          * eval:var:Parser
18455     */
18456     
18457     var Parser= function (options) {
18458       this.tokens = [];
18459       this.token = null;
18460       this.options = options || marked.defaults;
18461       this.options.renderer = this.options.renderer || new Renderer;
18462       this.renderer = this.options.renderer;
18463       this.renderer.options = this.options;
18464     }
18465     
18466     /**
18467      * Static Parse Method
18468      */
18469     
18470     Parser.parse = function(src, options, renderer) {
18471       var parser = new Parser(options, renderer);
18472       return parser.parse(src);
18473     };
18474     
18475     /**
18476      * Parse Loop
18477      */
18478     
18479     Parser.prototype.parse = function(src) {
18480       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18481       this.tokens = src.reverse();
18482     
18483       var out = '';
18484       while (this.next()) {
18485         out += this.tok();
18486       }
18487     
18488       return out;
18489     };
18490     
18491     /**
18492      * Next Token
18493      */
18494     
18495     Parser.prototype.next = function() {
18496       return this.token = this.tokens.pop();
18497     };
18498     
18499     /**
18500      * Preview Next Token
18501      */
18502     
18503     Parser.prototype.peek = function() {
18504       return this.tokens[this.tokens.length - 1] || 0;
18505     };
18506     
18507     /**
18508      * Parse Text Tokens
18509      */
18510     
18511     Parser.prototype.parseText = function() {
18512       var body = this.token.text;
18513     
18514       while (this.peek().type === 'text') {
18515         body += '\n' + this.next().text;
18516       }
18517     
18518       return this.inline.output(body);
18519     };
18520     
18521     /**
18522      * Parse Current Token
18523      */
18524     
18525     Parser.prototype.tok = function() {
18526       switch (this.token.type) {
18527         case 'space': {
18528           return '';
18529         }
18530         case 'hr': {
18531           return this.renderer.hr();
18532         }
18533         case 'heading': {
18534           return this.renderer.heading(
18535             this.inline.output(this.token.text),
18536             this.token.depth,
18537             this.token.text);
18538         }
18539         case 'code': {
18540           return this.renderer.code(this.token.text,
18541             this.token.lang,
18542             this.token.escaped);
18543         }
18544         case 'table': {
18545           var header = ''
18546             , body = ''
18547             , i
18548             , row
18549             , cell
18550             , flags
18551             , j;
18552     
18553           // header
18554           cell = '';
18555           for (i = 0; i < this.token.header.length; i++) {
18556             flags = { header: true, align: this.token.align[i] };
18557             cell += this.renderer.tablecell(
18558               this.inline.output(this.token.header[i]),
18559               { header: true, align: this.token.align[i] }
18560             );
18561           }
18562           header += this.renderer.tablerow(cell);
18563     
18564           for (i = 0; i < this.token.cells.length; i++) {
18565             row = this.token.cells[i];
18566     
18567             cell = '';
18568             for (j = 0; j < row.length; j++) {
18569               cell += this.renderer.tablecell(
18570                 this.inline.output(row[j]),
18571                 { header: false, align: this.token.align[j] }
18572               );
18573             }
18574     
18575             body += this.renderer.tablerow(cell);
18576           }
18577           return this.renderer.table(header, body);
18578         }
18579         case 'blockquote_start': {
18580           var body = '';
18581     
18582           while (this.next().type !== 'blockquote_end') {
18583             body += this.tok();
18584           }
18585     
18586           return this.renderer.blockquote(body);
18587         }
18588         case 'list_start': {
18589           var body = ''
18590             , ordered = this.token.ordered;
18591     
18592           while (this.next().type !== 'list_end') {
18593             body += this.tok();
18594           }
18595     
18596           return this.renderer.list(body, ordered);
18597         }
18598         case 'list_item_start': {
18599           var body = '';
18600     
18601           while (this.next().type !== 'list_item_end') {
18602             body += this.token.type === 'text'
18603               ? this.parseText()
18604               : this.tok();
18605           }
18606     
18607           return this.renderer.listitem(body);
18608         }
18609         case 'loose_item_start': {
18610           var body = '';
18611     
18612           while (this.next().type !== 'list_item_end') {
18613             body += this.tok();
18614           }
18615     
18616           return this.renderer.listitem(body);
18617         }
18618         case 'html': {
18619           var html = !this.token.pre && !this.options.pedantic
18620             ? this.inline.output(this.token.text)
18621             : this.token.text;
18622           return this.renderer.html(html);
18623         }
18624         case 'paragraph': {
18625           return this.renderer.paragraph(this.inline.output(this.token.text));
18626         }
18627         case 'text': {
18628           return this.renderer.paragraph(this.parseText());
18629         }
18630       }
18631     };
18632   
18633     
18634     /**
18635      * Marked
18636      */
18637          /**
18638          * eval:var:marked
18639     */
18640     var marked = function (src, opt, callback) {
18641       if (callback || typeof opt === 'function') {
18642         if (!callback) {
18643           callback = opt;
18644           opt = null;
18645         }
18646     
18647         opt = merge({}, marked.defaults, opt || {});
18648     
18649         var highlight = opt.highlight
18650           , tokens
18651           , pending
18652           , i = 0;
18653     
18654         try {
18655           tokens = Lexer.lex(src, opt)
18656         } catch (e) {
18657           return callback(e);
18658         }
18659     
18660         pending = tokens.length;
18661          /**
18662          * eval:var:done
18663     */
18664         var done = function(err) {
18665           if (err) {
18666             opt.highlight = highlight;
18667             return callback(err);
18668           }
18669     
18670           var out;
18671     
18672           try {
18673             out = Parser.parse(tokens, opt);
18674           } catch (e) {
18675             err = e;
18676           }
18677     
18678           opt.highlight = highlight;
18679     
18680           return err
18681             ? callback(err)
18682             : callback(null, out);
18683         };
18684     
18685         if (!highlight || highlight.length < 3) {
18686           return done();
18687         }
18688     
18689         delete opt.highlight;
18690     
18691         if (!pending) { return done(); }
18692     
18693         for (; i < tokens.length; i++) {
18694           (function(token) {
18695             if (token.type !== 'code') {
18696               return --pending || done();
18697             }
18698             return highlight(token.text, token.lang, function(err, code) {
18699               if (err) { return done(err); }
18700               if (code == null || code === token.text) {
18701                 return --pending || done();
18702               }
18703               token.text = code;
18704               token.escaped = true;
18705               --pending || done();
18706             });
18707           })(tokens[i]);
18708         }
18709     
18710         return;
18711       }
18712       try {
18713         if (opt) { opt = merge({}, marked.defaults, opt); }
18714         return Parser.parse(Lexer.lex(src, opt), opt);
18715       } catch (e) {
18716         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18717         if ((opt || marked.defaults).silent) {
18718           return '<p>An error occured:</p><pre>'
18719             + escape(e.message + '', true)
18720             + '</pre>';
18721         }
18722         throw e;
18723       }
18724     }
18725     
18726     /**
18727      * Options
18728      */
18729     
18730     marked.options =
18731     marked.setOptions = function(opt) {
18732       merge(marked.defaults, opt);
18733       return marked;
18734     };
18735     
18736     marked.defaults = {
18737       gfm: true,
18738       tables: true,
18739       breaks: false,
18740       pedantic: false,
18741       sanitize: false,
18742       sanitizer: null,
18743       mangle: true,
18744       smartLists: false,
18745       silent: false,
18746       highlight: null,
18747       langPrefix: 'lang-',
18748       smartypants: false,
18749       headerPrefix: '',
18750       renderer: new Renderer,
18751       xhtml: false
18752     };
18753     
18754     /**
18755      * Expose
18756      */
18757     
18758     marked.Parser = Parser;
18759     marked.parser = Parser.parse;
18760     
18761     marked.Renderer = Renderer;
18762     
18763     marked.Lexer = Lexer;
18764     marked.lexer = Lexer.lex;
18765     
18766     marked.InlineLexer = InlineLexer;
18767     marked.inlineLexer = InlineLexer.output;
18768     
18769     marked.parse = marked;
18770     
18771     Roo.Markdown.marked = marked;
18772
18773 })();/*
18774  * Based on:
18775  * Ext JS Library 1.1.1
18776  * Copyright(c) 2006-2007, Ext JS, LLC.
18777  *
18778  * Originally Released Under LGPL - original licence link has changed is not relivant.
18779  *
18780  * Fork - LGPL
18781  * <script type="text/javascript">
18782  */
18783
18784
18785
18786 /*
18787  * These classes are derivatives of the similarly named classes in the YUI Library.
18788  * The original license:
18789  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18790  * Code licensed under the BSD License:
18791  * http://developer.yahoo.net/yui/license.txt
18792  */
18793
18794 (function() {
18795
18796 var Event=Roo.EventManager;
18797 var Dom=Roo.lib.Dom;
18798
18799 /**
18800  * @class Roo.dd.DragDrop
18801  * @extends Roo.util.Observable
18802  * Defines the interface and base operation of items that that can be
18803  * dragged or can be drop targets.  It was designed to be extended, overriding
18804  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18805  * Up to three html elements can be associated with a DragDrop instance:
18806  * <ul>
18807  * <li>linked element: the element that is passed into the constructor.
18808  * This is the element which defines the boundaries for interaction with
18809  * other DragDrop objects.</li>
18810  * <li>handle element(s): The drag operation only occurs if the element that
18811  * was clicked matches a handle element.  By default this is the linked
18812  * element, but there are times that you will want only a portion of the
18813  * linked element to initiate the drag operation, and the setHandleElId()
18814  * method provides a way to define this.</li>
18815  * <li>drag element: this represents the element that would be moved along
18816  * with the cursor during a drag operation.  By default, this is the linked
18817  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18818  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18819  * </li>
18820  * </ul>
18821  * This class should not be instantiated until the onload event to ensure that
18822  * the associated elements are available.
18823  * The following would define a DragDrop obj that would interact with any
18824  * other DragDrop obj in the "group1" group:
18825  * <pre>
18826  *  dd = new Roo.dd.DragDrop("div1", "group1");
18827  * </pre>
18828  * Since none of the event handlers have been implemented, nothing would
18829  * actually happen if you were to run the code above.  Normally you would
18830  * override this class or one of the default implementations, but you can
18831  * also override the methods you want on an instance of the class...
18832  * <pre>
18833  *  dd.onDragDrop = function(e, id) {
18834  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18835  *  }
18836  * </pre>
18837  * @constructor
18838  * @param {String} id of the element that is linked to this instance
18839  * @param {String} sGroup the group of related DragDrop objects
18840  * @param {object} config an object containing configurable attributes
18841  *                Valid properties for DragDrop:
18842  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18843  */
18844 Roo.dd.DragDrop = function(id, sGroup, config) {
18845     if (id) {
18846         this.init(id, sGroup, config);
18847     }
18848     
18849 };
18850
18851 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18852
18853     /**
18854      * The id of the element associated with this object.  This is what we
18855      * refer to as the "linked element" because the size and position of
18856      * this element is used to determine when the drag and drop objects have
18857      * interacted.
18858      * @property id
18859      * @type String
18860      */
18861     id: null,
18862
18863     /**
18864      * Configuration attributes passed into the constructor
18865      * @property config
18866      * @type object
18867      */
18868     config: null,
18869
18870     /**
18871      * The id of the element that will be dragged.  By default this is same
18872      * as the linked element , but could be changed to another element. Ex:
18873      * Roo.dd.DDProxy
18874      * @property dragElId
18875      * @type String
18876      * @private
18877      */
18878     dragElId: null,
18879
18880     /**
18881      * the id of the element that initiates the drag operation.  By default
18882      * this is the linked element, but could be changed to be a child of this
18883      * element.  This lets us do things like only starting the drag when the
18884      * header element within the linked html element is clicked.
18885      * @property handleElId
18886      * @type String
18887      * @private
18888      */
18889     handleElId: null,
18890
18891     /**
18892      * An associative array of HTML tags that will be ignored if clicked.
18893      * @property invalidHandleTypes
18894      * @type {string: string}
18895      */
18896     invalidHandleTypes: null,
18897
18898     /**
18899      * An associative array of ids for elements that will be ignored if clicked
18900      * @property invalidHandleIds
18901      * @type {string: string}
18902      */
18903     invalidHandleIds: null,
18904
18905     /**
18906      * An indexted array of css class names for elements that will be ignored
18907      * if clicked.
18908      * @property invalidHandleClasses
18909      * @type string[]
18910      */
18911     invalidHandleClasses: null,
18912
18913     /**
18914      * The linked element's absolute X position at the time the drag was
18915      * started
18916      * @property startPageX
18917      * @type int
18918      * @private
18919      */
18920     startPageX: 0,
18921
18922     /**
18923      * The linked element's absolute X position at the time the drag was
18924      * started
18925      * @property startPageY
18926      * @type int
18927      * @private
18928      */
18929     startPageY: 0,
18930
18931     /**
18932      * The group defines a logical collection of DragDrop objects that are
18933      * related.  Instances only get events when interacting with other
18934      * DragDrop object in the same group.  This lets us define multiple
18935      * groups using a single DragDrop subclass if we want.
18936      * @property groups
18937      * @type {string: string}
18938      */
18939     groups: null,
18940
18941     /**
18942      * Individual drag/drop instances can be locked.  This will prevent
18943      * onmousedown start drag.
18944      * @property locked
18945      * @type boolean
18946      * @private
18947      */
18948     locked: false,
18949
18950     /**
18951      * Lock this instance
18952      * @method lock
18953      */
18954     lock: function() { this.locked = true; },
18955
18956     /**
18957      * Unlock this instace
18958      * @method unlock
18959      */
18960     unlock: function() { this.locked = false; },
18961
18962     /**
18963      * By default, all insances can be a drop target.  This can be disabled by
18964      * setting isTarget to false.
18965      * @method isTarget
18966      * @type boolean
18967      */
18968     isTarget: true,
18969
18970     /**
18971      * The padding configured for this drag and drop object for calculating
18972      * the drop zone intersection with this object.
18973      * @method padding
18974      * @type int[]
18975      */
18976     padding: null,
18977
18978     /**
18979      * Cached reference to the linked element
18980      * @property _domRef
18981      * @private
18982      */
18983     _domRef: null,
18984
18985     /**
18986      * Internal typeof flag
18987      * @property __ygDragDrop
18988      * @private
18989      */
18990     __ygDragDrop: true,
18991
18992     /**
18993      * Set to true when horizontal contraints are applied
18994      * @property constrainX
18995      * @type boolean
18996      * @private
18997      */
18998     constrainX: false,
18999
19000     /**
19001      * Set to true when vertical contraints are applied
19002      * @property constrainY
19003      * @type boolean
19004      * @private
19005      */
19006     constrainY: false,
19007
19008     /**
19009      * The left constraint
19010      * @property minX
19011      * @type int
19012      * @private
19013      */
19014     minX: 0,
19015
19016     /**
19017      * The right constraint
19018      * @property maxX
19019      * @type int
19020      * @private
19021      */
19022     maxX: 0,
19023
19024     /**
19025      * The up constraint
19026      * @property minY
19027      * @type int
19028      * @type int
19029      * @private
19030      */
19031     minY: 0,
19032
19033     /**
19034      * The down constraint
19035      * @property maxY
19036      * @type int
19037      * @private
19038      */
19039     maxY: 0,
19040
19041     /**
19042      * Maintain offsets when we resetconstraints.  Set to true when you want
19043      * the position of the element relative to its parent to stay the same
19044      * when the page changes
19045      *
19046      * @property maintainOffset
19047      * @type boolean
19048      */
19049     maintainOffset: false,
19050
19051     /**
19052      * Array of pixel locations the element will snap to if we specified a
19053      * horizontal graduation/interval.  This array is generated automatically
19054      * when you define a tick interval.
19055      * @property xTicks
19056      * @type int[]
19057      */
19058     xTicks: null,
19059
19060     /**
19061      * Array of pixel locations the element will snap to if we specified a
19062      * vertical graduation/interval.  This array is generated automatically
19063      * when you define a tick interval.
19064      * @property yTicks
19065      * @type int[]
19066      */
19067     yTicks: null,
19068
19069     /**
19070      * By default the drag and drop instance will only respond to the primary
19071      * button click (left button for a right-handed mouse).  Set to true to
19072      * allow drag and drop to start with any mouse click that is propogated
19073      * by the browser
19074      * @property primaryButtonOnly
19075      * @type boolean
19076      */
19077     primaryButtonOnly: true,
19078
19079     /**
19080      * The availabe property is false until the linked dom element is accessible.
19081      * @property available
19082      * @type boolean
19083      */
19084     available: false,
19085
19086     /**
19087      * By default, drags can only be initiated if the mousedown occurs in the
19088      * region the linked element is.  This is done in part to work around a
19089      * bug in some browsers that mis-report the mousedown if the previous
19090      * mouseup happened outside of the window.  This property is set to true
19091      * if outer handles are defined.
19092      *
19093      * @property hasOuterHandles
19094      * @type boolean
19095      * @default false
19096      */
19097     hasOuterHandles: false,
19098
19099     /**
19100      * Code that executes immediately before the startDrag event
19101      * @method b4StartDrag
19102      * @private
19103      */
19104     b4StartDrag: function(x, y) { },
19105
19106     /**
19107      * Abstract method called after a drag/drop object is clicked
19108      * and the drag or mousedown time thresholds have beeen met.
19109      * @method startDrag
19110      * @param {int} X click location
19111      * @param {int} Y click location
19112      */
19113     startDrag: function(x, y) { /* override this */ },
19114
19115     /**
19116      * Code that executes immediately before the onDrag event
19117      * @method b4Drag
19118      * @private
19119      */
19120     b4Drag: function(e) { },
19121
19122     /**
19123      * Abstract method called during the onMouseMove event while dragging an
19124      * object.
19125      * @method onDrag
19126      * @param {Event} e the mousemove event
19127      */
19128     onDrag: function(e) { /* override this */ },
19129
19130     /**
19131      * Abstract method called when this element fist begins hovering over
19132      * another DragDrop obj
19133      * @method onDragEnter
19134      * @param {Event} e the mousemove event
19135      * @param {String|DragDrop[]} id In POINT mode, the element
19136      * id this is hovering over.  In INTERSECT mode, an array of one or more
19137      * dragdrop items being hovered over.
19138      */
19139     onDragEnter: function(e, id) { /* override this */ },
19140
19141     /**
19142      * Code that executes immediately before the onDragOver event
19143      * @method b4DragOver
19144      * @private
19145      */
19146     b4DragOver: function(e) { },
19147
19148     /**
19149      * Abstract method called when this element is hovering over another
19150      * DragDrop obj
19151      * @method onDragOver
19152      * @param {Event} e the mousemove event
19153      * @param {String|DragDrop[]} id In POINT mode, the element
19154      * id this is hovering over.  In INTERSECT mode, an array of dd items
19155      * being hovered over.
19156      */
19157     onDragOver: function(e, id) { /* override this */ },
19158
19159     /**
19160      * Code that executes immediately before the onDragOut event
19161      * @method b4DragOut
19162      * @private
19163      */
19164     b4DragOut: function(e) { },
19165
19166     /**
19167      * Abstract method called when we are no longer hovering over an element
19168      * @method onDragOut
19169      * @param {Event} e the mousemove event
19170      * @param {String|DragDrop[]} id In POINT mode, the element
19171      * id this was hovering over.  In INTERSECT mode, an array of dd items
19172      * that the mouse is no longer over.
19173      */
19174     onDragOut: function(e, id) { /* override this */ },
19175
19176     /**
19177      * Code that executes immediately before the onDragDrop event
19178      * @method b4DragDrop
19179      * @private
19180      */
19181     b4DragDrop: function(e) { },
19182
19183     /**
19184      * Abstract method called when this item is dropped on another DragDrop
19185      * obj
19186      * @method onDragDrop
19187      * @param {Event} e the mouseup event
19188      * @param {String|DragDrop[]} id In POINT mode, the element
19189      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19190      * was dropped on.
19191      */
19192     onDragDrop: function(e, id) { /* override this */ },
19193
19194     /**
19195      * Abstract method called when this item is dropped on an area with no
19196      * drop target
19197      * @method onInvalidDrop
19198      * @param {Event} e the mouseup event
19199      */
19200     onInvalidDrop: function(e) { /* override this */ },
19201
19202     /**
19203      * Code that executes immediately before the endDrag event
19204      * @method b4EndDrag
19205      * @private
19206      */
19207     b4EndDrag: function(e) { },
19208
19209     /**
19210      * Fired when we are done dragging the object
19211      * @method endDrag
19212      * @param {Event} e the mouseup event
19213      */
19214     endDrag: function(e) { /* override this */ },
19215
19216     /**
19217      * Code executed immediately before the onMouseDown event
19218      * @method b4MouseDown
19219      * @param {Event} e the mousedown event
19220      * @private
19221      */
19222     b4MouseDown: function(e) {  },
19223
19224     /**
19225      * Event handler that fires when a drag/drop obj gets a mousedown
19226      * @method onMouseDown
19227      * @param {Event} e the mousedown event
19228      */
19229     onMouseDown: function(e) { /* override this */ },
19230
19231     /**
19232      * Event handler that fires when a drag/drop obj gets a mouseup
19233      * @method onMouseUp
19234      * @param {Event} e the mouseup event
19235      */
19236     onMouseUp: function(e) { /* override this */ },
19237
19238     /**
19239      * Override the onAvailable method to do what is needed after the initial
19240      * position was determined.
19241      * @method onAvailable
19242      */
19243     onAvailable: function () {
19244     },
19245
19246     /*
19247      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19248      * @type Object
19249      */
19250     defaultPadding : {left:0, right:0, top:0, bottom:0},
19251
19252     /*
19253      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19254  *
19255  * Usage:
19256  <pre><code>
19257  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19258                 { dragElId: "existingProxyDiv" });
19259  dd.startDrag = function(){
19260      this.constrainTo("parent-id");
19261  };
19262  </code></pre>
19263  * Or you can initalize it using the {@link Roo.Element} object:
19264  <pre><code>
19265  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19266      startDrag : function(){
19267          this.constrainTo("parent-id");
19268      }
19269  });
19270  </code></pre>
19271      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19272      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19273      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19274      * an object containing the sides to pad. For example: {right:10, bottom:10}
19275      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19276      */
19277     constrainTo : function(constrainTo, pad, inContent){
19278         if(typeof pad == "number"){
19279             pad = {left: pad, right:pad, top:pad, bottom:pad};
19280         }
19281         pad = pad || this.defaultPadding;
19282         var b = Roo.get(this.getEl()).getBox();
19283         var ce = Roo.get(constrainTo);
19284         var s = ce.getScroll();
19285         var c, cd = ce.dom;
19286         if(cd == document.body){
19287             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19288         }else{
19289             xy = ce.getXY();
19290             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19291         }
19292
19293
19294         var topSpace = b.y - c.y;
19295         var leftSpace = b.x - c.x;
19296
19297         this.resetConstraints();
19298         this.setXConstraint(leftSpace - (pad.left||0), // left
19299                 c.width - leftSpace - b.width - (pad.right||0) //right
19300         );
19301         this.setYConstraint(topSpace - (pad.top||0), //top
19302                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19303         );
19304     },
19305
19306     /**
19307      * Returns a reference to the linked element
19308      * @method getEl
19309      * @return {HTMLElement} the html element
19310      */
19311     getEl: function() {
19312         if (!this._domRef) {
19313             this._domRef = Roo.getDom(this.id);
19314         }
19315
19316         return this._domRef;
19317     },
19318
19319     /**
19320      * Returns a reference to the actual element to drag.  By default this is
19321      * the same as the html element, but it can be assigned to another
19322      * element. An example of this can be found in Roo.dd.DDProxy
19323      * @method getDragEl
19324      * @return {HTMLElement} the html element
19325      */
19326     getDragEl: function() {
19327         return Roo.getDom(this.dragElId);
19328     },
19329
19330     /**
19331      * Sets up the DragDrop object.  Must be called in the constructor of any
19332      * Roo.dd.DragDrop subclass
19333      * @method init
19334      * @param id the id of the linked element
19335      * @param {String} sGroup the group of related items
19336      * @param {object} config configuration attributes
19337      */
19338     init: function(id, sGroup, config) {
19339         this.initTarget(id, sGroup, config);
19340         if (!Roo.isTouch) {
19341             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19342         }
19343         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19344         // Event.on(this.id, "selectstart", Event.preventDefault);
19345     },
19346
19347     /**
19348      * Initializes Targeting functionality only... the object does not
19349      * get a mousedown handler.
19350      * @method initTarget
19351      * @param id the id of the linked element
19352      * @param {String} sGroup the group of related items
19353      * @param {object} config configuration attributes
19354      */
19355     initTarget: function(id, sGroup, config) {
19356
19357         // configuration attributes
19358         this.config = config || {};
19359
19360         // create a local reference to the drag and drop manager
19361         this.DDM = Roo.dd.DDM;
19362         // initialize the groups array
19363         this.groups = {};
19364
19365         // assume that we have an element reference instead of an id if the
19366         // parameter is not a string
19367         if (typeof id !== "string") {
19368             id = Roo.id(id);
19369         }
19370
19371         // set the id
19372         this.id = id;
19373
19374         // add to an interaction group
19375         this.addToGroup((sGroup) ? sGroup : "default");
19376
19377         // We don't want to register this as the handle with the manager
19378         // so we just set the id rather than calling the setter.
19379         this.handleElId = id;
19380
19381         // the linked element is the element that gets dragged by default
19382         this.setDragElId(id);
19383
19384         // by default, clicked anchors will not start drag operations.
19385         this.invalidHandleTypes = { A: "A" };
19386         this.invalidHandleIds = {};
19387         this.invalidHandleClasses = [];
19388
19389         this.applyConfig();
19390
19391         this.handleOnAvailable();
19392     },
19393
19394     /**
19395      * Applies the configuration parameters that were passed into the constructor.
19396      * This is supposed to happen at each level through the inheritance chain.  So
19397      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19398      * DragDrop in order to get all of the parameters that are available in
19399      * each object.
19400      * @method applyConfig
19401      */
19402     applyConfig: function() {
19403
19404         // configurable properties:
19405         //    padding, isTarget, maintainOffset, primaryButtonOnly
19406         this.padding           = this.config.padding || [0, 0, 0, 0];
19407         this.isTarget          = (this.config.isTarget !== false);
19408         this.maintainOffset    = (this.config.maintainOffset);
19409         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19410
19411     },
19412
19413     /**
19414      * Executed when the linked element is available
19415      * @method handleOnAvailable
19416      * @private
19417      */
19418     handleOnAvailable: function() {
19419         this.available = true;
19420         this.resetConstraints();
19421         this.onAvailable();
19422     },
19423
19424      /**
19425      * Configures the padding for the target zone in px.  Effectively expands
19426      * (or reduces) the virtual object size for targeting calculations.
19427      * Supports css-style shorthand; if only one parameter is passed, all sides
19428      * will have that padding, and if only two are passed, the top and bottom
19429      * will have the first param, the left and right the second.
19430      * @method setPadding
19431      * @param {int} iTop    Top pad
19432      * @param {int} iRight  Right pad
19433      * @param {int} iBot    Bot pad
19434      * @param {int} iLeft   Left pad
19435      */
19436     setPadding: function(iTop, iRight, iBot, iLeft) {
19437         // this.padding = [iLeft, iRight, iTop, iBot];
19438         if (!iRight && 0 !== iRight) {
19439             this.padding = [iTop, iTop, iTop, iTop];
19440         } else if (!iBot && 0 !== iBot) {
19441             this.padding = [iTop, iRight, iTop, iRight];
19442         } else {
19443             this.padding = [iTop, iRight, iBot, iLeft];
19444         }
19445     },
19446
19447     /**
19448      * Stores the initial placement of the linked element.
19449      * @method setInitialPosition
19450      * @param {int} diffX   the X offset, default 0
19451      * @param {int} diffY   the Y offset, default 0
19452      */
19453     setInitPosition: function(diffX, diffY) {
19454         var el = this.getEl();
19455
19456         if (!this.DDM.verifyEl(el)) {
19457             return;
19458         }
19459
19460         var dx = diffX || 0;
19461         var dy = diffY || 0;
19462
19463         var p = Dom.getXY( el );
19464
19465         this.initPageX = p[0] - dx;
19466         this.initPageY = p[1] - dy;
19467
19468         this.lastPageX = p[0];
19469         this.lastPageY = p[1];
19470
19471
19472         this.setStartPosition(p);
19473     },
19474
19475     /**
19476      * Sets the start position of the element.  This is set when the obj
19477      * is initialized, the reset when a drag is started.
19478      * @method setStartPosition
19479      * @param pos current position (from previous lookup)
19480      * @private
19481      */
19482     setStartPosition: function(pos) {
19483         var p = pos || Dom.getXY( this.getEl() );
19484         this.deltaSetXY = null;
19485
19486         this.startPageX = p[0];
19487         this.startPageY = p[1];
19488     },
19489
19490     /**
19491      * Add this instance to a group of related drag/drop objects.  All
19492      * instances belong to at least one group, and can belong to as many
19493      * groups as needed.
19494      * @method addToGroup
19495      * @param sGroup {string} the name of the group
19496      */
19497     addToGroup: function(sGroup) {
19498         this.groups[sGroup] = true;
19499         this.DDM.regDragDrop(this, sGroup);
19500     },
19501
19502     /**
19503      * Remove's this instance from the supplied interaction group
19504      * @method removeFromGroup
19505      * @param {string}  sGroup  The group to drop
19506      */
19507     removeFromGroup: function(sGroup) {
19508         if (this.groups[sGroup]) {
19509             delete this.groups[sGroup];
19510         }
19511
19512         this.DDM.removeDDFromGroup(this, sGroup);
19513     },
19514
19515     /**
19516      * Allows you to specify that an element other than the linked element
19517      * will be moved with the cursor during a drag
19518      * @method setDragElId
19519      * @param id {string} the id of the element that will be used to initiate the drag
19520      */
19521     setDragElId: function(id) {
19522         this.dragElId = id;
19523     },
19524
19525     /**
19526      * Allows you to specify a child of the linked element that should be
19527      * used to initiate the drag operation.  An example of this would be if
19528      * you have a content div with text and links.  Clicking anywhere in the
19529      * content area would normally start the drag operation.  Use this method
19530      * to specify that an element inside of the content div is the element
19531      * that starts the drag operation.
19532      * @method setHandleElId
19533      * @param id {string} the id of the element that will be used to
19534      * initiate the drag.
19535      */
19536     setHandleElId: function(id) {
19537         if (typeof id !== "string") {
19538             id = Roo.id(id);
19539         }
19540         this.handleElId = id;
19541         this.DDM.regHandle(this.id, id);
19542     },
19543
19544     /**
19545      * Allows you to set an element outside of the linked element as a drag
19546      * handle
19547      * @method setOuterHandleElId
19548      * @param id the id of the element that will be used to initiate the drag
19549      */
19550     setOuterHandleElId: function(id) {
19551         if (typeof id !== "string") {
19552             id = Roo.id(id);
19553         }
19554         Event.on(id, "mousedown",
19555                 this.handleMouseDown, this);
19556         this.setHandleElId(id);
19557
19558         this.hasOuterHandles = true;
19559     },
19560
19561     /**
19562      * Remove all drag and drop hooks for this element
19563      * @method unreg
19564      */
19565     unreg: function() {
19566         Event.un(this.id, "mousedown",
19567                 this.handleMouseDown);
19568         Event.un(this.id, "touchstart",
19569                 this.handleMouseDown);
19570         this._domRef = null;
19571         this.DDM._remove(this);
19572     },
19573
19574     destroy : function(){
19575         this.unreg();
19576     },
19577
19578     /**
19579      * Returns true if this instance is locked, or the drag drop mgr is locked
19580      * (meaning that all drag/drop is disabled on the page.)
19581      * @method isLocked
19582      * @return {boolean} true if this obj or all drag/drop is locked, else
19583      * false
19584      */
19585     isLocked: function() {
19586         return (this.DDM.isLocked() || this.locked);
19587     },
19588
19589     /**
19590      * Fired when this object is clicked
19591      * @method handleMouseDown
19592      * @param {Event} e
19593      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19594      * @private
19595      */
19596     handleMouseDown: function(e, oDD){
19597      
19598         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19599             //Roo.log('not touch/ button !=0');
19600             return;
19601         }
19602         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19603             return; // double touch..
19604         }
19605         
19606
19607         if (this.isLocked()) {
19608             //Roo.log('locked');
19609             return;
19610         }
19611
19612         this.DDM.refreshCache(this.groups);
19613 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19614         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19615         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19616             //Roo.log('no outer handes or not over target');
19617                 // do nothing.
19618         } else {
19619 //            Roo.log('check validator');
19620             if (this.clickValidator(e)) {
19621 //                Roo.log('validate success');
19622                 // set the initial element position
19623                 this.setStartPosition();
19624
19625
19626                 this.b4MouseDown(e);
19627                 this.onMouseDown(e);
19628
19629                 this.DDM.handleMouseDown(e, this);
19630
19631                 this.DDM.stopEvent(e);
19632             } else {
19633
19634
19635             }
19636         }
19637     },
19638
19639     clickValidator: function(e) {
19640         var target = e.getTarget();
19641         return ( this.isValidHandleChild(target) &&
19642                     (this.id == this.handleElId ||
19643                         this.DDM.handleWasClicked(target, this.id)) );
19644     },
19645
19646     /**
19647      * Allows you to specify a tag name that should not start a drag operation
19648      * when clicked.  This is designed to facilitate embedding links within a
19649      * drag handle that do something other than start the drag.
19650      * @method addInvalidHandleType
19651      * @param {string} tagName the type of element to exclude
19652      */
19653     addInvalidHandleType: function(tagName) {
19654         var type = tagName.toUpperCase();
19655         this.invalidHandleTypes[type] = type;
19656     },
19657
19658     /**
19659      * Lets you to specify an element id for a child of a drag handle
19660      * that should not initiate a drag
19661      * @method addInvalidHandleId
19662      * @param {string} id the element id of the element you wish to ignore
19663      */
19664     addInvalidHandleId: function(id) {
19665         if (typeof id !== "string") {
19666             id = Roo.id(id);
19667         }
19668         this.invalidHandleIds[id] = id;
19669     },
19670
19671     /**
19672      * Lets you specify a css class of elements that will not initiate a drag
19673      * @method addInvalidHandleClass
19674      * @param {string} cssClass the class of the elements you wish to ignore
19675      */
19676     addInvalidHandleClass: function(cssClass) {
19677         this.invalidHandleClasses.push(cssClass);
19678     },
19679
19680     /**
19681      * Unsets an excluded tag name set by addInvalidHandleType
19682      * @method removeInvalidHandleType
19683      * @param {string} tagName the type of element to unexclude
19684      */
19685     removeInvalidHandleType: function(tagName) {
19686         var type = tagName.toUpperCase();
19687         // this.invalidHandleTypes[type] = null;
19688         delete this.invalidHandleTypes[type];
19689     },
19690
19691     /**
19692      * Unsets an invalid handle id
19693      * @method removeInvalidHandleId
19694      * @param {string} id the id of the element to re-enable
19695      */
19696     removeInvalidHandleId: function(id) {
19697         if (typeof id !== "string") {
19698             id = Roo.id(id);
19699         }
19700         delete this.invalidHandleIds[id];
19701     },
19702
19703     /**
19704      * Unsets an invalid css class
19705      * @method removeInvalidHandleClass
19706      * @param {string} cssClass the class of the element(s) you wish to
19707      * re-enable
19708      */
19709     removeInvalidHandleClass: function(cssClass) {
19710         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19711             if (this.invalidHandleClasses[i] == cssClass) {
19712                 delete this.invalidHandleClasses[i];
19713             }
19714         }
19715     },
19716
19717     /**
19718      * Checks the tag exclusion list to see if this click should be ignored
19719      * @method isValidHandleChild
19720      * @param {HTMLElement} node the HTMLElement to evaluate
19721      * @return {boolean} true if this is a valid tag type, false if not
19722      */
19723     isValidHandleChild: function(node) {
19724
19725         var valid = true;
19726         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19727         var nodeName;
19728         try {
19729             nodeName = node.nodeName.toUpperCase();
19730         } catch(e) {
19731             nodeName = node.nodeName;
19732         }
19733         valid = valid && !this.invalidHandleTypes[nodeName];
19734         valid = valid && !this.invalidHandleIds[node.id];
19735
19736         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19737             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19738         }
19739
19740
19741         return valid;
19742
19743     },
19744
19745     /**
19746      * Create the array of horizontal tick marks if an interval was specified
19747      * in setXConstraint().
19748      * @method setXTicks
19749      * @private
19750      */
19751     setXTicks: function(iStartX, iTickSize) {
19752         this.xTicks = [];
19753         this.xTickSize = iTickSize;
19754
19755         var tickMap = {};
19756
19757         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19758             if (!tickMap[i]) {
19759                 this.xTicks[this.xTicks.length] = i;
19760                 tickMap[i] = true;
19761             }
19762         }
19763
19764         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19765             if (!tickMap[i]) {
19766                 this.xTicks[this.xTicks.length] = i;
19767                 tickMap[i] = true;
19768             }
19769         }
19770
19771         this.xTicks.sort(this.DDM.numericSort) ;
19772     },
19773
19774     /**
19775      * Create the array of vertical tick marks if an interval was specified in
19776      * setYConstraint().
19777      * @method setYTicks
19778      * @private
19779      */
19780     setYTicks: function(iStartY, iTickSize) {
19781         this.yTicks = [];
19782         this.yTickSize = iTickSize;
19783
19784         var tickMap = {};
19785
19786         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19787             if (!tickMap[i]) {
19788                 this.yTicks[this.yTicks.length] = i;
19789                 tickMap[i] = true;
19790             }
19791         }
19792
19793         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19794             if (!tickMap[i]) {
19795                 this.yTicks[this.yTicks.length] = i;
19796                 tickMap[i] = true;
19797             }
19798         }
19799
19800         this.yTicks.sort(this.DDM.numericSort) ;
19801     },
19802
19803     /**
19804      * By default, the element can be dragged any place on the screen.  Use
19805      * this method to limit the horizontal travel of the element.  Pass in
19806      * 0,0 for the parameters if you want to lock the drag to the y axis.
19807      * @method setXConstraint
19808      * @param {int} iLeft the number of pixels the element can move to the left
19809      * @param {int} iRight the number of pixels the element can move to the
19810      * right
19811      * @param {int} iTickSize optional parameter for specifying that the
19812      * element
19813      * should move iTickSize pixels at a time.
19814      */
19815     setXConstraint: function(iLeft, iRight, iTickSize) {
19816         this.leftConstraint = iLeft;
19817         this.rightConstraint = iRight;
19818
19819         this.minX = this.initPageX - iLeft;
19820         this.maxX = this.initPageX + iRight;
19821         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19822
19823         this.constrainX = true;
19824     },
19825
19826     /**
19827      * Clears any constraints applied to this instance.  Also clears ticks
19828      * since they can't exist independent of a constraint at this time.
19829      * @method clearConstraints
19830      */
19831     clearConstraints: function() {
19832         this.constrainX = false;
19833         this.constrainY = false;
19834         this.clearTicks();
19835     },
19836
19837     /**
19838      * Clears any tick interval defined for this instance
19839      * @method clearTicks
19840      */
19841     clearTicks: function() {
19842         this.xTicks = null;
19843         this.yTicks = null;
19844         this.xTickSize = 0;
19845         this.yTickSize = 0;
19846     },
19847
19848     /**
19849      * By default, the element can be dragged any place on the screen.  Set
19850      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19851      * parameters if you want to lock the drag to the x axis.
19852      * @method setYConstraint
19853      * @param {int} iUp the number of pixels the element can move up
19854      * @param {int} iDown the number of pixels the element can move down
19855      * @param {int} iTickSize optional parameter for specifying that the
19856      * element should move iTickSize pixels at a time.
19857      */
19858     setYConstraint: function(iUp, iDown, iTickSize) {
19859         this.topConstraint = iUp;
19860         this.bottomConstraint = iDown;
19861
19862         this.minY = this.initPageY - iUp;
19863         this.maxY = this.initPageY + iDown;
19864         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19865
19866         this.constrainY = true;
19867
19868     },
19869
19870     /**
19871      * resetConstraints must be called if you manually reposition a dd element.
19872      * @method resetConstraints
19873      * @param {boolean} maintainOffset
19874      */
19875     resetConstraints: function() {
19876
19877
19878         // Maintain offsets if necessary
19879         if (this.initPageX || this.initPageX === 0) {
19880             // figure out how much this thing has moved
19881             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19882             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19883
19884             this.setInitPosition(dx, dy);
19885
19886         // This is the first time we have detected the element's position
19887         } else {
19888             this.setInitPosition();
19889         }
19890
19891         if (this.constrainX) {
19892             this.setXConstraint( this.leftConstraint,
19893                                  this.rightConstraint,
19894                                  this.xTickSize        );
19895         }
19896
19897         if (this.constrainY) {
19898             this.setYConstraint( this.topConstraint,
19899                                  this.bottomConstraint,
19900                                  this.yTickSize         );
19901         }
19902     },
19903
19904     /**
19905      * Normally the drag element is moved pixel by pixel, but we can specify
19906      * that it move a number of pixels at a time.  This method resolves the
19907      * location when we have it set up like this.
19908      * @method getTick
19909      * @param {int} val where we want to place the object
19910      * @param {int[]} tickArray sorted array of valid points
19911      * @return {int} the closest tick
19912      * @private
19913      */
19914     getTick: function(val, tickArray) {
19915
19916         if (!tickArray) {
19917             // If tick interval is not defined, it is effectively 1 pixel,
19918             // so we return the value passed to us.
19919             return val;
19920         } else if (tickArray[0] >= val) {
19921             // The value is lower than the first tick, so we return the first
19922             // tick.
19923             return tickArray[0];
19924         } else {
19925             for (var i=0, len=tickArray.length; i<len; ++i) {
19926                 var next = i + 1;
19927                 if (tickArray[next] && tickArray[next] >= val) {
19928                     var diff1 = val - tickArray[i];
19929                     var diff2 = tickArray[next] - val;
19930                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19931                 }
19932             }
19933
19934             // The value is larger than the last tick, so we return the last
19935             // tick.
19936             return tickArray[tickArray.length - 1];
19937         }
19938     },
19939
19940     /**
19941      * toString method
19942      * @method toString
19943      * @return {string} string representation of the dd obj
19944      */
19945     toString: function() {
19946         return ("DragDrop " + this.id);
19947     }
19948
19949 });
19950
19951 })();
19952 /*
19953  * Based on:
19954  * Ext JS Library 1.1.1
19955  * Copyright(c) 2006-2007, Ext JS, LLC.
19956  *
19957  * Originally Released Under LGPL - original licence link has changed is not relivant.
19958  *
19959  * Fork - LGPL
19960  * <script type="text/javascript">
19961  */
19962
19963
19964 /**
19965  * The drag and drop utility provides a framework for building drag and drop
19966  * applications.  In addition to enabling drag and drop for specific elements,
19967  * the drag and drop elements are tracked by the manager class, and the
19968  * interactions between the various elements are tracked during the drag and
19969  * the implementing code is notified about these important moments.
19970  */
19971
19972 // Only load the library once.  Rewriting the manager class would orphan
19973 // existing drag and drop instances.
19974 if (!Roo.dd.DragDropMgr) {
19975
19976 /**
19977  * @class Roo.dd.DragDropMgr
19978  * DragDropMgr is a singleton that tracks the element interaction for
19979  * all DragDrop items in the window.  Generally, you will not call
19980  * this class directly, but it does have helper methods that could
19981  * be useful in your DragDrop implementations.
19982  * @singleton
19983  */
19984 Roo.dd.DragDropMgr = function() {
19985
19986     var Event = Roo.EventManager;
19987
19988     return {
19989
19990         /**
19991          * Two dimensional Array of registered DragDrop objects.  The first
19992          * dimension is the DragDrop item group, the second the DragDrop
19993          * object.
19994          * @property ids
19995          * @type {string: string}
19996          * @private
19997          * @static
19998          */
19999         ids: {},
20000
20001         /**
20002          * Array of element ids defined as drag handles.  Used to determine
20003          * if the element that generated the mousedown event is actually the
20004          * handle and not the html element itself.
20005          * @property handleIds
20006          * @type {string: string}
20007          * @private
20008          * @static
20009          */
20010         handleIds: {},
20011
20012         /**
20013          * the DragDrop object that is currently being dragged
20014          * @property dragCurrent
20015          * @type DragDrop
20016          * @private
20017          * @static
20018          **/
20019         dragCurrent: null,
20020
20021         /**
20022          * the DragDrop object(s) that are being hovered over
20023          * @property dragOvers
20024          * @type Array
20025          * @private
20026          * @static
20027          */
20028         dragOvers: {},
20029
20030         /**
20031          * the X distance between the cursor and the object being dragged
20032          * @property deltaX
20033          * @type int
20034          * @private
20035          * @static
20036          */
20037         deltaX: 0,
20038
20039         /**
20040          * the Y distance between the cursor and the object being dragged
20041          * @property deltaY
20042          * @type int
20043          * @private
20044          * @static
20045          */
20046         deltaY: 0,
20047
20048         /**
20049          * Flag to determine if we should prevent the default behavior of the
20050          * events we define. By default this is true, but this can be set to
20051          * false if you need the default behavior (not recommended)
20052          * @property preventDefault
20053          * @type boolean
20054          * @static
20055          */
20056         preventDefault: true,
20057
20058         /**
20059          * Flag to determine if we should stop the propagation of the events
20060          * we generate. This is true by default but you may want to set it to
20061          * false if the html element contains other features that require the
20062          * mouse click.
20063          * @property stopPropagation
20064          * @type boolean
20065          * @static
20066          */
20067         stopPropagation: true,
20068
20069         /**
20070          * Internal flag that is set to true when drag and drop has been
20071          * intialized
20072          * @property initialized
20073          * @private
20074          * @static
20075          */
20076         initalized: false,
20077
20078         /**
20079          * All drag and drop can be disabled.
20080          * @property locked
20081          * @private
20082          * @static
20083          */
20084         locked: false,
20085
20086         /**
20087          * Called the first time an element is registered.
20088          * @method init
20089          * @private
20090          * @static
20091          */
20092         init: function() {
20093             this.initialized = true;
20094         },
20095
20096         /**
20097          * In point mode, drag and drop interaction is defined by the
20098          * location of the cursor during the drag/drop
20099          * @property POINT
20100          * @type int
20101          * @static
20102          */
20103         POINT: 0,
20104
20105         /**
20106          * In intersect mode, drag and drop interactio nis defined by the
20107          * overlap of two or more drag and drop objects.
20108          * @property INTERSECT
20109          * @type int
20110          * @static
20111          */
20112         INTERSECT: 1,
20113
20114         /**
20115          * The current drag and drop mode.  Default: POINT
20116          * @property mode
20117          * @type int
20118          * @static
20119          */
20120         mode: 0,
20121
20122         /**
20123          * Runs method on all drag and drop objects
20124          * @method _execOnAll
20125          * @private
20126          * @static
20127          */
20128         _execOnAll: function(sMethod, args) {
20129             for (var i in this.ids) {
20130                 for (var j in this.ids[i]) {
20131                     var oDD = this.ids[i][j];
20132                     if (! this.isTypeOfDD(oDD)) {
20133                         continue;
20134                     }
20135                     oDD[sMethod].apply(oDD, args);
20136                 }
20137             }
20138         },
20139
20140         /**
20141          * Drag and drop initialization.  Sets up the global event handlers
20142          * @method _onLoad
20143          * @private
20144          * @static
20145          */
20146         _onLoad: function() {
20147
20148             this.init();
20149
20150             if (!Roo.isTouch) {
20151                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20152                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20153             }
20154             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20155             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20156             
20157             Event.on(window,   "unload",    this._onUnload, this, true);
20158             Event.on(window,   "resize",    this._onResize, this, true);
20159             // Event.on(window,   "mouseout",    this._test);
20160
20161         },
20162
20163         /**
20164          * Reset constraints on all drag and drop objs
20165          * @method _onResize
20166          * @private
20167          * @static
20168          */
20169         _onResize: function(e) {
20170             this._execOnAll("resetConstraints", []);
20171         },
20172
20173         /**
20174          * Lock all drag and drop functionality
20175          * @method lock
20176          * @static
20177          */
20178         lock: function() { this.locked = true; },
20179
20180         /**
20181          * Unlock all drag and drop functionality
20182          * @method unlock
20183          * @static
20184          */
20185         unlock: function() { this.locked = false; },
20186
20187         /**
20188          * Is drag and drop locked?
20189          * @method isLocked
20190          * @return {boolean} True if drag and drop is locked, false otherwise.
20191          * @static
20192          */
20193         isLocked: function() { return this.locked; },
20194
20195         /**
20196          * Location cache that is set for all drag drop objects when a drag is
20197          * initiated, cleared when the drag is finished.
20198          * @property locationCache
20199          * @private
20200          * @static
20201          */
20202         locationCache: {},
20203
20204         /**
20205          * Set useCache to false if you want to force object the lookup of each
20206          * drag and drop linked element constantly during a drag.
20207          * @property useCache
20208          * @type boolean
20209          * @static
20210          */
20211         useCache: true,
20212
20213         /**
20214          * The number of pixels that the mouse needs to move after the
20215          * mousedown before the drag is initiated.  Default=3;
20216          * @property clickPixelThresh
20217          * @type int
20218          * @static
20219          */
20220         clickPixelThresh: 3,
20221
20222         /**
20223          * The number of milliseconds after the mousedown event to initiate the
20224          * drag if we don't get a mouseup event. Default=1000
20225          * @property clickTimeThresh
20226          * @type int
20227          * @static
20228          */
20229         clickTimeThresh: 350,
20230
20231         /**
20232          * Flag that indicates that either the drag pixel threshold or the
20233          * mousdown time threshold has been met
20234          * @property dragThreshMet
20235          * @type boolean
20236          * @private
20237          * @static
20238          */
20239         dragThreshMet: false,
20240
20241         /**
20242          * Timeout used for the click time threshold
20243          * @property clickTimeout
20244          * @type Object
20245          * @private
20246          * @static
20247          */
20248         clickTimeout: null,
20249
20250         /**
20251          * The X position of the mousedown event stored for later use when a
20252          * drag threshold is met.
20253          * @property startX
20254          * @type int
20255          * @private
20256          * @static
20257          */
20258         startX: 0,
20259
20260         /**
20261          * The Y position of the mousedown event stored for later use when a
20262          * drag threshold is met.
20263          * @property startY
20264          * @type int
20265          * @private
20266          * @static
20267          */
20268         startY: 0,
20269
20270         /**
20271          * Each DragDrop instance must be registered with the DragDropMgr.
20272          * This is executed in DragDrop.init()
20273          * @method regDragDrop
20274          * @param {DragDrop} oDD the DragDrop object to register
20275          * @param {String} sGroup the name of the group this element belongs to
20276          * @static
20277          */
20278         regDragDrop: function(oDD, sGroup) {
20279             if (!this.initialized) { this.init(); }
20280
20281             if (!this.ids[sGroup]) {
20282                 this.ids[sGroup] = {};
20283             }
20284             this.ids[sGroup][oDD.id] = oDD;
20285         },
20286
20287         /**
20288          * Removes the supplied dd instance from the supplied group. Executed
20289          * by DragDrop.removeFromGroup, so don't call this function directly.
20290          * @method removeDDFromGroup
20291          * @private
20292          * @static
20293          */
20294         removeDDFromGroup: function(oDD, sGroup) {
20295             if (!this.ids[sGroup]) {
20296                 this.ids[sGroup] = {};
20297             }
20298
20299             var obj = this.ids[sGroup];
20300             if (obj && obj[oDD.id]) {
20301                 delete obj[oDD.id];
20302             }
20303         },
20304
20305         /**
20306          * Unregisters a drag and drop item.  This is executed in
20307          * DragDrop.unreg, use that method instead of calling this directly.
20308          * @method _remove
20309          * @private
20310          * @static
20311          */
20312         _remove: function(oDD) {
20313             for (var g in oDD.groups) {
20314                 if (g && this.ids[g][oDD.id]) {
20315                     delete this.ids[g][oDD.id];
20316                 }
20317             }
20318             delete this.handleIds[oDD.id];
20319         },
20320
20321         /**
20322          * Each DragDrop handle element must be registered.  This is done
20323          * automatically when executing DragDrop.setHandleElId()
20324          * @method regHandle
20325          * @param {String} sDDId the DragDrop id this element is a handle for
20326          * @param {String} sHandleId the id of the element that is the drag
20327          * handle
20328          * @static
20329          */
20330         regHandle: function(sDDId, sHandleId) {
20331             if (!this.handleIds[sDDId]) {
20332                 this.handleIds[sDDId] = {};
20333             }
20334             this.handleIds[sDDId][sHandleId] = sHandleId;
20335         },
20336
20337         /**
20338          * Utility function to determine if a given element has been
20339          * registered as a drag drop item.
20340          * @method isDragDrop
20341          * @param {String} id the element id to check
20342          * @return {boolean} true if this element is a DragDrop item,
20343          * false otherwise
20344          * @static
20345          */
20346         isDragDrop: function(id) {
20347             return ( this.getDDById(id) ) ? true : false;
20348         },
20349
20350         /**
20351          * Returns the drag and drop instances that are in all groups the
20352          * passed in instance belongs to.
20353          * @method getRelated
20354          * @param {DragDrop} p_oDD the obj to get related data for
20355          * @param {boolean} bTargetsOnly if true, only return targetable objs
20356          * @return {DragDrop[]} the related instances
20357          * @static
20358          */
20359         getRelated: function(p_oDD, bTargetsOnly) {
20360             var oDDs = [];
20361             for (var i in p_oDD.groups) {
20362                 for (j in this.ids[i]) {
20363                     var dd = this.ids[i][j];
20364                     if (! this.isTypeOfDD(dd)) {
20365                         continue;
20366                     }
20367                     if (!bTargetsOnly || dd.isTarget) {
20368                         oDDs[oDDs.length] = dd;
20369                     }
20370                 }
20371             }
20372
20373             return oDDs;
20374         },
20375
20376         /**
20377          * Returns true if the specified dd target is a legal target for
20378          * the specifice drag obj
20379          * @method isLegalTarget
20380          * @param {DragDrop} the drag obj
20381          * @param {DragDrop} the target
20382          * @return {boolean} true if the target is a legal target for the
20383          * dd obj
20384          * @static
20385          */
20386         isLegalTarget: function (oDD, oTargetDD) {
20387             var targets = this.getRelated(oDD, true);
20388             for (var i=0, len=targets.length;i<len;++i) {
20389                 if (targets[i].id == oTargetDD.id) {
20390                     return true;
20391                 }
20392             }
20393
20394             return false;
20395         },
20396
20397         /**
20398          * My goal is to be able to transparently determine if an object is
20399          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20400          * returns "object", oDD.constructor.toString() always returns
20401          * "DragDrop" and not the name of the subclass.  So for now it just
20402          * evaluates a well-known variable in DragDrop.
20403          * @method isTypeOfDD
20404          * @param {Object} the object to evaluate
20405          * @return {boolean} true if typeof oDD = DragDrop
20406          * @static
20407          */
20408         isTypeOfDD: function (oDD) {
20409             return (oDD && oDD.__ygDragDrop);
20410         },
20411
20412         /**
20413          * Utility function to determine if a given element has been
20414          * registered as a drag drop handle for the given Drag Drop object.
20415          * @method isHandle
20416          * @param {String} id the element id to check
20417          * @return {boolean} true if this element is a DragDrop handle, false
20418          * otherwise
20419          * @static
20420          */
20421         isHandle: function(sDDId, sHandleId) {
20422             return ( this.handleIds[sDDId] &&
20423                             this.handleIds[sDDId][sHandleId] );
20424         },
20425
20426         /**
20427          * Returns the DragDrop instance for a given id
20428          * @method getDDById
20429          * @param {String} id the id of the DragDrop object
20430          * @return {DragDrop} the drag drop object, null if it is not found
20431          * @static
20432          */
20433         getDDById: function(id) {
20434             for (var i in this.ids) {
20435                 if (this.ids[i][id]) {
20436                     return this.ids[i][id];
20437                 }
20438             }
20439             return null;
20440         },
20441
20442         /**
20443          * Fired after a registered DragDrop object gets the mousedown event.
20444          * Sets up the events required to track the object being dragged
20445          * @method handleMouseDown
20446          * @param {Event} e the event
20447          * @param oDD the DragDrop object being dragged
20448          * @private
20449          * @static
20450          */
20451         handleMouseDown: function(e, oDD) {
20452             if(Roo.QuickTips){
20453                 Roo.QuickTips.disable();
20454             }
20455             this.currentTarget = e.getTarget();
20456
20457             this.dragCurrent = oDD;
20458
20459             var el = oDD.getEl();
20460
20461             // track start position
20462             this.startX = e.getPageX();
20463             this.startY = e.getPageY();
20464
20465             this.deltaX = this.startX - el.offsetLeft;
20466             this.deltaY = this.startY - el.offsetTop;
20467
20468             this.dragThreshMet = false;
20469
20470             this.clickTimeout = setTimeout(
20471                     function() {
20472                         var DDM = Roo.dd.DDM;
20473                         DDM.startDrag(DDM.startX, DDM.startY);
20474                     },
20475                     this.clickTimeThresh );
20476         },
20477
20478         /**
20479          * Fired when either the drag pixel threshol or the mousedown hold
20480          * time threshold has been met.
20481          * @method startDrag
20482          * @param x {int} the X position of the original mousedown
20483          * @param y {int} the Y position of the original mousedown
20484          * @static
20485          */
20486         startDrag: function(x, y) {
20487             clearTimeout(this.clickTimeout);
20488             if (this.dragCurrent) {
20489                 this.dragCurrent.b4StartDrag(x, y);
20490                 this.dragCurrent.startDrag(x, y);
20491             }
20492             this.dragThreshMet = true;
20493         },
20494
20495         /**
20496          * Internal function to handle the mouseup event.  Will be invoked
20497          * from the context of the document.
20498          * @method handleMouseUp
20499          * @param {Event} e the event
20500          * @private
20501          * @static
20502          */
20503         handleMouseUp: function(e) {
20504
20505             if(Roo.QuickTips){
20506                 Roo.QuickTips.enable();
20507             }
20508             if (! this.dragCurrent) {
20509                 return;
20510             }
20511
20512             clearTimeout(this.clickTimeout);
20513
20514             if (this.dragThreshMet) {
20515                 this.fireEvents(e, true);
20516             } else {
20517             }
20518
20519             this.stopDrag(e);
20520
20521             this.stopEvent(e);
20522         },
20523
20524         /**
20525          * Utility to stop event propagation and event default, if these
20526          * features are turned on.
20527          * @method stopEvent
20528          * @param {Event} e the event as returned by this.getEvent()
20529          * @static
20530          */
20531         stopEvent: function(e){
20532             if(this.stopPropagation) {
20533                 e.stopPropagation();
20534             }
20535
20536             if (this.preventDefault) {
20537                 e.preventDefault();
20538             }
20539         },
20540
20541         /**
20542          * Internal function to clean up event handlers after the drag
20543          * operation is complete
20544          * @method stopDrag
20545          * @param {Event} e the event
20546          * @private
20547          * @static
20548          */
20549         stopDrag: function(e) {
20550             // Fire the drag end event for the item that was dragged
20551             if (this.dragCurrent) {
20552                 if (this.dragThreshMet) {
20553                     this.dragCurrent.b4EndDrag(e);
20554                     this.dragCurrent.endDrag(e);
20555                 }
20556
20557                 this.dragCurrent.onMouseUp(e);
20558             }
20559
20560             this.dragCurrent = null;
20561             this.dragOvers = {};
20562         },
20563
20564         /**
20565          * Internal function to handle the mousemove event.  Will be invoked
20566          * from the context of the html element.
20567          *
20568          * @TODO figure out what we can do about mouse events lost when the
20569          * user drags objects beyond the window boundary.  Currently we can
20570          * detect this in internet explorer by verifying that the mouse is
20571          * down during the mousemove event.  Firefox doesn't give us the
20572          * button state on the mousemove event.
20573          * @method handleMouseMove
20574          * @param {Event} e the event
20575          * @private
20576          * @static
20577          */
20578         handleMouseMove: function(e) {
20579             if (! this.dragCurrent) {
20580                 return true;
20581             }
20582
20583             // var button = e.which || e.button;
20584
20585             // check for IE mouseup outside of page boundary
20586             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20587                 this.stopEvent(e);
20588                 return this.handleMouseUp(e);
20589             }
20590
20591             if (!this.dragThreshMet) {
20592                 var diffX = Math.abs(this.startX - e.getPageX());
20593                 var diffY = Math.abs(this.startY - e.getPageY());
20594                 if (diffX > this.clickPixelThresh ||
20595                             diffY > this.clickPixelThresh) {
20596                     this.startDrag(this.startX, this.startY);
20597                 }
20598             }
20599
20600             if (this.dragThreshMet) {
20601                 this.dragCurrent.b4Drag(e);
20602                 this.dragCurrent.onDrag(e);
20603                 if(!this.dragCurrent.moveOnly){
20604                     this.fireEvents(e, false);
20605                 }
20606             }
20607
20608             this.stopEvent(e);
20609
20610             return true;
20611         },
20612
20613         /**
20614          * Iterates over all of the DragDrop elements to find ones we are
20615          * hovering over or dropping on
20616          * @method fireEvents
20617          * @param {Event} e the event
20618          * @param {boolean} isDrop is this a drop op or a mouseover op?
20619          * @private
20620          * @static
20621          */
20622         fireEvents: function(e, isDrop) {
20623             var dc = this.dragCurrent;
20624
20625             // If the user did the mouse up outside of the window, we could
20626             // get here even though we have ended the drag.
20627             if (!dc || dc.isLocked()) {
20628                 return;
20629             }
20630
20631             var pt = e.getPoint();
20632
20633             // cache the previous dragOver array
20634             var oldOvers = [];
20635
20636             var outEvts   = [];
20637             var overEvts  = [];
20638             var dropEvts  = [];
20639             var enterEvts = [];
20640
20641             // Check to see if the object(s) we were hovering over is no longer
20642             // being hovered over so we can fire the onDragOut event
20643             for (var i in this.dragOvers) {
20644
20645                 var ddo = this.dragOvers[i];
20646
20647                 if (! this.isTypeOfDD(ddo)) {
20648                     continue;
20649                 }
20650
20651                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20652                     outEvts.push( ddo );
20653                 }
20654
20655                 oldOvers[i] = true;
20656                 delete this.dragOvers[i];
20657             }
20658
20659             for (var sGroup in dc.groups) {
20660
20661                 if ("string" != typeof sGroup) {
20662                     continue;
20663                 }
20664
20665                 for (i in this.ids[sGroup]) {
20666                     var oDD = this.ids[sGroup][i];
20667                     if (! this.isTypeOfDD(oDD)) {
20668                         continue;
20669                     }
20670
20671                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20672                         if (this.isOverTarget(pt, oDD, this.mode)) {
20673                             // look for drop interactions
20674                             if (isDrop) {
20675                                 dropEvts.push( oDD );
20676                             // look for drag enter and drag over interactions
20677                             } else {
20678
20679                                 // initial drag over: dragEnter fires
20680                                 if (!oldOvers[oDD.id]) {
20681                                     enterEvts.push( oDD );
20682                                 // subsequent drag overs: dragOver fires
20683                                 } else {
20684                                     overEvts.push( oDD );
20685                                 }
20686
20687                                 this.dragOvers[oDD.id] = oDD;
20688                             }
20689                         }
20690                     }
20691                 }
20692             }
20693
20694             if (this.mode) {
20695                 if (outEvts.length) {
20696                     dc.b4DragOut(e, outEvts);
20697                     dc.onDragOut(e, outEvts);
20698                 }
20699
20700                 if (enterEvts.length) {
20701                     dc.onDragEnter(e, enterEvts);
20702                 }
20703
20704                 if (overEvts.length) {
20705                     dc.b4DragOver(e, overEvts);
20706                     dc.onDragOver(e, overEvts);
20707                 }
20708
20709                 if (dropEvts.length) {
20710                     dc.b4DragDrop(e, dropEvts);
20711                     dc.onDragDrop(e, dropEvts);
20712                 }
20713
20714             } else {
20715                 // fire dragout events
20716                 var len = 0;
20717                 for (i=0, len=outEvts.length; i<len; ++i) {
20718                     dc.b4DragOut(e, outEvts[i].id);
20719                     dc.onDragOut(e, outEvts[i].id);
20720                 }
20721
20722                 // fire enter events
20723                 for (i=0,len=enterEvts.length; i<len; ++i) {
20724                     // dc.b4DragEnter(e, oDD.id);
20725                     dc.onDragEnter(e, enterEvts[i].id);
20726                 }
20727
20728                 // fire over events
20729                 for (i=0,len=overEvts.length; i<len; ++i) {
20730                     dc.b4DragOver(e, overEvts[i].id);
20731                     dc.onDragOver(e, overEvts[i].id);
20732                 }
20733
20734                 // fire drop events
20735                 for (i=0, len=dropEvts.length; i<len; ++i) {
20736                     dc.b4DragDrop(e, dropEvts[i].id);
20737                     dc.onDragDrop(e, dropEvts[i].id);
20738                 }
20739
20740             }
20741
20742             // notify about a drop that did not find a target
20743             if (isDrop && !dropEvts.length) {
20744                 dc.onInvalidDrop(e);
20745             }
20746
20747         },
20748
20749         /**
20750          * Helper function for getting the best match from the list of drag
20751          * and drop objects returned by the drag and drop events when we are
20752          * in INTERSECT mode.  It returns either the first object that the
20753          * cursor is over, or the object that has the greatest overlap with
20754          * the dragged element.
20755          * @method getBestMatch
20756          * @param  {DragDrop[]} dds The array of drag and drop objects
20757          * targeted
20758          * @return {DragDrop}       The best single match
20759          * @static
20760          */
20761         getBestMatch: function(dds) {
20762             var winner = null;
20763             // Return null if the input is not what we expect
20764             //if (!dds || !dds.length || dds.length == 0) {
20765                // winner = null;
20766             // If there is only one item, it wins
20767             //} else if (dds.length == 1) {
20768
20769             var len = dds.length;
20770
20771             if (len == 1) {
20772                 winner = dds[0];
20773             } else {
20774                 // Loop through the targeted items
20775                 for (var i=0; i<len; ++i) {
20776                     var dd = dds[i];
20777                     // If the cursor is over the object, it wins.  If the
20778                     // cursor is over multiple matches, the first one we come
20779                     // to wins.
20780                     if (dd.cursorIsOver) {
20781                         winner = dd;
20782                         break;
20783                     // Otherwise the object with the most overlap wins
20784                     } else {
20785                         if (!winner ||
20786                             winner.overlap.getArea() < dd.overlap.getArea()) {
20787                             winner = dd;
20788                         }
20789                     }
20790                 }
20791             }
20792
20793             return winner;
20794         },
20795
20796         /**
20797          * Refreshes the cache of the top-left and bottom-right points of the
20798          * drag and drop objects in the specified group(s).  This is in the
20799          * format that is stored in the drag and drop instance, so typical
20800          * usage is:
20801          * <code>
20802          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20803          * </code>
20804          * Alternatively:
20805          * <code>
20806          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20807          * </code>
20808          * @TODO this really should be an indexed array.  Alternatively this
20809          * method could accept both.
20810          * @method refreshCache
20811          * @param {Object} groups an associative array of groups to refresh
20812          * @static
20813          */
20814         refreshCache: function(groups) {
20815             for (var sGroup in groups) {
20816                 if ("string" != typeof sGroup) {
20817                     continue;
20818                 }
20819                 for (var i in this.ids[sGroup]) {
20820                     var oDD = this.ids[sGroup][i];
20821
20822                     if (this.isTypeOfDD(oDD)) {
20823                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20824                         var loc = this.getLocation(oDD);
20825                         if (loc) {
20826                             this.locationCache[oDD.id] = loc;
20827                         } else {
20828                             delete this.locationCache[oDD.id];
20829                             // this will unregister the drag and drop object if
20830                             // the element is not in a usable state
20831                             // oDD.unreg();
20832                         }
20833                     }
20834                 }
20835             }
20836         },
20837
20838         /**
20839          * This checks to make sure an element exists and is in the DOM.  The
20840          * main purpose is to handle cases where innerHTML is used to remove
20841          * drag and drop objects from the DOM.  IE provides an 'unspecified
20842          * error' when trying to access the offsetParent of such an element
20843          * @method verifyEl
20844          * @param {HTMLElement} el the element to check
20845          * @return {boolean} true if the element looks usable
20846          * @static
20847          */
20848         verifyEl: function(el) {
20849             if (el) {
20850                 var parent;
20851                 if(Roo.isIE){
20852                     try{
20853                         parent = el.offsetParent;
20854                     }catch(e){}
20855                 }else{
20856                     parent = el.offsetParent;
20857                 }
20858                 if (parent) {
20859                     return true;
20860                 }
20861             }
20862
20863             return false;
20864         },
20865
20866         /**
20867          * Returns a Region object containing the drag and drop element's position
20868          * and size, including the padding configured for it
20869          * @method getLocation
20870          * @param {DragDrop} oDD the drag and drop object to get the
20871          *                       location for
20872          * @return {Roo.lib.Region} a Region object representing the total area
20873          *                             the element occupies, including any padding
20874          *                             the instance is configured for.
20875          * @static
20876          */
20877         getLocation: function(oDD) {
20878             if (! this.isTypeOfDD(oDD)) {
20879                 return null;
20880             }
20881
20882             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20883
20884             try {
20885                 pos= Roo.lib.Dom.getXY(el);
20886             } catch (e) { }
20887
20888             if (!pos) {
20889                 return null;
20890             }
20891
20892             x1 = pos[0];
20893             x2 = x1 + el.offsetWidth;
20894             y1 = pos[1];
20895             y2 = y1 + el.offsetHeight;
20896
20897             t = y1 - oDD.padding[0];
20898             r = x2 + oDD.padding[1];
20899             b = y2 + oDD.padding[2];
20900             l = x1 - oDD.padding[3];
20901
20902             return new Roo.lib.Region( t, r, b, l );
20903         },
20904
20905         /**
20906          * Checks the cursor location to see if it over the target
20907          * @method isOverTarget
20908          * @param {Roo.lib.Point} pt The point to evaluate
20909          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20910          * @return {boolean} true if the mouse is over the target
20911          * @private
20912          * @static
20913          */
20914         isOverTarget: function(pt, oTarget, intersect) {
20915             // use cache if available
20916             var loc = this.locationCache[oTarget.id];
20917             if (!loc || !this.useCache) {
20918                 loc = this.getLocation(oTarget);
20919                 this.locationCache[oTarget.id] = loc;
20920
20921             }
20922
20923             if (!loc) {
20924                 return false;
20925             }
20926
20927             oTarget.cursorIsOver = loc.contains( pt );
20928
20929             // DragDrop is using this as a sanity check for the initial mousedown
20930             // in this case we are done.  In POINT mode, if the drag obj has no
20931             // contraints, we are also done. Otherwise we need to evaluate the
20932             // location of the target as related to the actual location of the
20933             // dragged element.
20934             var dc = this.dragCurrent;
20935             if (!dc || !dc.getTargetCoord ||
20936                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20937                 return oTarget.cursorIsOver;
20938             }
20939
20940             oTarget.overlap = null;
20941
20942             // Get the current location of the drag element, this is the
20943             // location of the mouse event less the delta that represents
20944             // where the original mousedown happened on the element.  We
20945             // need to consider constraints and ticks as well.
20946             var pos = dc.getTargetCoord(pt.x, pt.y);
20947
20948             var el = dc.getDragEl();
20949             var curRegion = new Roo.lib.Region( pos.y,
20950                                                    pos.x + el.offsetWidth,
20951                                                    pos.y + el.offsetHeight,
20952                                                    pos.x );
20953
20954             var overlap = curRegion.intersect(loc);
20955
20956             if (overlap) {
20957                 oTarget.overlap = overlap;
20958                 return (intersect) ? true : oTarget.cursorIsOver;
20959             } else {
20960                 return false;
20961             }
20962         },
20963
20964         /**
20965          * unload event handler
20966          * @method _onUnload
20967          * @private
20968          * @static
20969          */
20970         _onUnload: function(e, me) {
20971             Roo.dd.DragDropMgr.unregAll();
20972         },
20973
20974         /**
20975          * Cleans up the drag and drop events and objects.
20976          * @method unregAll
20977          * @private
20978          * @static
20979          */
20980         unregAll: function() {
20981
20982             if (this.dragCurrent) {
20983                 this.stopDrag();
20984                 this.dragCurrent = null;
20985             }
20986
20987             this._execOnAll("unreg", []);
20988
20989             for (i in this.elementCache) {
20990                 delete this.elementCache[i];
20991             }
20992
20993             this.elementCache = {};
20994             this.ids = {};
20995         },
20996
20997         /**
20998          * A cache of DOM elements
20999          * @property elementCache
21000          * @private
21001          * @static
21002          */
21003         elementCache: {},
21004
21005         /**
21006          * Get the wrapper for the DOM element specified
21007          * @method getElWrapper
21008          * @param {String} id the id of the element to get
21009          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21010          * @private
21011          * @deprecated This wrapper isn't that useful
21012          * @static
21013          */
21014         getElWrapper: function(id) {
21015             var oWrapper = this.elementCache[id];
21016             if (!oWrapper || !oWrapper.el) {
21017                 oWrapper = this.elementCache[id] =
21018                     new this.ElementWrapper(Roo.getDom(id));
21019             }
21020             return oWrapper;
21021         },
21022
21023         /**
21024          * Returns the actual DOM element
21025          * @method getElement
21026          * @param {String} id the id of the elment to get
21027          * @return {Object} The element
21028          * @deprecated use Roo.getDom instead
21029          * @static
21030          */
21031         getElement: function(id) {
21032             return Roo.getDom(id);
21033         },
21034
21035         /**
21036          * Returns the style property for the DOM element (i.e.,
21037          * document.getElById(id).style)
21038          * @method getCss
21039          * @param {String} id the id of the elment to get
21040          * @return {Object} The style property of the element
21041          * @deprecated use Roo.getDom instead
21042          * @static
21043          */
21044         getCss: function(id) {
21045             var el = Roo.getDom(id);
21046             return (el) ? el.style : null;
21047         },
21048
21049         /**
21050          * Inner class for cached elements
21051          * @class DragDropMgr.ElementWrapper
21052          * @for DragDropMgr
21053          * @private
21054          * @deprecated
21055          */
21056         ElementWrapper: function(el) {
21057                 /**
21058                  * The element
21059                  * @property el
21060                  */
21061                 this.el = el || null;
21062                 /**
21063                  * The element id
21064                  * @property id
21065                  */
21066                 this.id = this.el && el.id;
21067                 /**
21068                  * A reference to the style property
21069                  * @property css
21070                  */
21071                 this.css = this.el && el.style;
21072             },
21073
21074         /**
21075          * Returns the X position of an html element
21076          * @method getPosX
21077          * @param el the element for which to get the position
21078          * @return {int} the X coordinate
21079          * @for DragDropMgr
21080          * @deprecated use Roo.lib.Dom.getX instead
21081          * @static
21082          */
21083         getPosX: function(el) {
21084             return Roo.lib.Dom.getX(el);
21085         },
21086
21087         /**
21088          * Returns the Y position of an html element
21089          * @method getPosY
21090          * @param el the element for which to get the position
21091          * @return {int} the Y coordinate
21092          * @deprecated use Roo.lib.Dom.getY instead
21093          * @static
21094          */
21095         getPosY: function(el) {
21096             return Roo.lib.Dom.getY(el);
21097         },
21098
21099         /**
21100          * Swap two nodes.  In IE, we use the native method, for others we
21101          * emulate the IE behavior
21102          * @method swapNode
21103          * @param n1 the first node to swap
21104          * @param n2 the other node to swap
21105          * @static
21106          */
21107         swapNode: function(n1, n2) {
21108             if (n1.swapNode) {
21109                 n1.swapNode(n2);
21110             } else {
21111                 var p = n2.parentNode;
21112                 var s = n2.nextSibling;
21113
21114                 if (s == n1) {
21115                     p.insertBefore(n1, n2);
21116                 } else if (n2 == n1.nextSibling) {
21117                     p.insertBefore(n2, n1);
21118                 } else {
21119                     n1.parentNode.replaceChild(n2, n1);
21120                     p.insertBefore(n1, s);
21121                 }
21122             }
21123         },
21124
21125         /**
21126          * Returns the current scroll position
21127          * @method getScroll
21128          * @private
21129          * @static
21130          */
21131         getScroll: function () {
21132             var t, l, dde=document.documentElement, db=document.body;
21133             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21134                 t = dde.scrollTop;
21135                 l = dde.scrollLeft;
21136             } else if (db) {
21137                 t = db.scrollTop;
21138                 l = db.scrollLeft;
21139             } else {
21140
21141             }
21142             return { top: t, left: l };
21143         },
21144
21145         /**
21146          * Returns the specified element style property
21147          * @method getStyle
21148          * @param {HTMLElement} el          the element
21149          * @param {string}      styleProp   the style property
21150          * @return {string} The value of the style property
21151          * @deprecated use Roo.lib.Dom.getStyle
21152          * @static
21153          */
21154         getStyle: function(el, styleProp) {
21155             return Roo.fly(el).getStyle(styleProp);
21156         },
21157
21158         /**
21159          * Gets the scrollTop
21160          * @method getScrollTop
21161          * @return {int} the document's scrollTop
21162          * @static
21163          */
21164         getScrollTop: function () { return this.getScroll().top; },
21165
21166         /**
21167          * Gets the scrollLeft
21168          * @method getScrollLeft
21169          * @return {int} the document's scrollTop
21170          * @static
21171          */
21172         getScrollLeft: function () { return this.getScroll().left; },
21173
21174         /**
21175          * Sets the x/y position of an element to the location of the
21176          * target element.
21177          * @method moveToEl
21178          * @param {HTMLElement} moveEl      The element to move
21179          * @param {HTMLElement} targetEl    The position reference element
21180          * @static
21181          */
21182         moveToEl: function (moveEl, targetEl) {
21183             var aCoord = Roo.lib.Dom.getXY(targetEl);
21184             Roo.lib.Dom.setXY(moveEl, aCoord);
21185         },
21186
21187         /**
21188          * Numeric array sort function
21189          * @method numericSort
21190          * @static
21191          */
21192         numericSort: function(a, b) { return (a - b); },
21193
21194         /**
21195          * Internal counter
21196          * @property _timeoutCount
21197          * @private
21198          * @static
21199          */
21200         _timeoutCount: 0,
21201
21202         /**
21203          * Trying to make the load order less important.  Without this we get
21204          * an error if this file is loaded before the Event Utility.
21205          * @method _addListeners
21206          * @private
21207          * @static
21208          */
21209         _addListeners: function() {
21210             var DDM = Roo.dd.DDM;
21211             if ( Roo.lib.Event && document ) {
21212                 DDM._onLoad();
21213             } else {
21214                 if (DDM._timeoutCount > 2000) {
21215                 } else {
21216                     setTimeout(DDM._addListeners, 10);
21217                     if (document && document.body) {
21218                         DDM._timeoutCount += 1;
21219                     }
21220                 }
21221             }
21222         },
21223
21224         /**
21225          * Recursively searches the immediate parent and all child nodes for
21226          * the handle element in order to determine wheter or not it was
21227          * clicked.
21228          * @method handleWasClicked
21229          * @param node the html element to inspect
21230          * @static
21231          */
21232         handleWasClicked: function(node, id) {
21233             if (this.isHandle(id, node.id)) {
21234                 return true;
21235             } else {
21236                 // check to see if this is a text node child of the one we want
21237                 var p = node.parentNode;
21238
21239                 while (p) {
21240                     if (this.isHandle(id, p.id)) {
21241                         return true;
21242                     } else {
21243                         p = p.parentNode;
21244                     }
21245                 }
21246             }
21247
21248             return false;
21249         }
21250
21251     };
21252
21253 }();
21254
21255 // shorter alias, save a few bytes
21256 Roo.dd.DDM = Roo.dd.DragDropMgr;
21257 Roo.dd.DDM._addListeners();
21258
21259 }/*
21260  * Based on:
21261  * Ext JS Library 1.1.1
21262  * Copyright(c) 2006-2007, Ext JS, LLC.
21263  *
21264  * Originally Released Under LGPL - original licence link has changed is not relivant.
21265  *
21266  * Fork - LGPL
21267  * <script type="text/javascript">
21268  */
21269
21270 /**
21271  * @class Roo.dd.DD
21272  * A DragDrop implementation where the linked element follows the
21273  * mouse cursor during a drag.
21274  * @extends Roo.dd.DragDrop
21275  * @constructor
21276  * @param {String} id the id of the linked element
21277  * @param {String} sGroup the group of related DragDrop items
21278  * @param {object} config an object containing configurable attributes
21279  *                Valid properties for DD:
21280  *                    scroll
21281  */
21282 Roo.dd.DD = function(id, sGroup, config) {
21283     if (id) {
21284         this.init(id, sGroup, config);
21285     }
21286 };
21287
21288 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21289
21290     /**
21291      * When set to true, the utility automatically tries to scroll the browser
21292      * window wehn a drag and drop element is dragged near the viewport boundary.
21293      * Defaults to true.
21294      * @property scroll
21295      * @type boolean
21296      */
21297     scroll: true,
21298
21299     /**
21300      * Sets the pointer offset to the distance between the linked element's top
21301      * left corner and the location the element was clicked
21302      * @method autoOffset
21303      * @param {int} iPageX the X coordinate of the click
21304      * @param {int} iPageY the Y coordinate of the click
21305      */
21306     autoOffset: function(iPageX, iPageY) {
21307         var x = iPageX - this.startPageX;
21308         var y = iPageY - this.startPageY;
21309         this.setDelta(x, y);
21310     },
21311
21312     /**
21313      * Sets the pointer offset.  You can call this directly to force the
21314      * offset to be in a particular location (e.g., pass in 0,0 to set it
21315      * to the center of the object)
21316      * @method setDelta
21317      * @param {int} iDeltaX the distance from the left
21318      * @param {int} iDeltaY the distance from the top
21319      */
21320     setDelta: function(iDeltaX, iDeltaY) {
21321         this.deltaX = iDeltaX;
21322         this.deltaY = iDeltaY;
21323     },
21324
21325     /**
21326      * Sets the drag element to the location of the mousedown or click event,
21327      * maintaining the cursor location relative to the location on the element
21328      * that was clicked.  Override this if you want to place the element in a
21329      * location other than where the cursor is.
21330      * @method setDragElPos
21331      * @param {int} iPageX the X coordinate of the mousedown or drag event
21332      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21333      */
21334     setDragElPos: function(iPageX, iPageY) {
21335         // the first time we do this, we are going to check to make sure
21336         // the element has css positioning
21337
21338         var el = this.getDragEl();
21339         this.alignElWithMouse(el, iPageX, iPageY);
21340     },
21341
21342     /**
21343      * Sets the element to the location of the mousedown or click event,
21344      * maintaining the cursor location relative to the location on the element
21345      * that was clicked.  Override this if you want to place the element in a
21346      * location other than where the cursor is.
21347      * @method alignElWithMouse
21348      * @param {HTMLElement} el the element to move
21349      * @param {int} iPageX the X coordinate of the mousedown or drag event
21350      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21351      */
21352     alignElWithMouse: function(el, iPageX, iPageY) {
21353         var oCoord = this.getTargetCoord(iPageX, iPageY);
21354         var fly = el.dom ? el : Roo.fly(el);
21355         if (!this.deltaSetXY) {
21356             var aCoord = [oCoord.x, oCoord.y];
21357             fly.setXY(aCoord);
21358             var newLeft = fly.getLeft(true);
21359             var newTop  = fly.getTop(true);
21360             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21361         } else {
21362             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21363         }
21364
21365         this.cachePosition(oCoord.x, oCoord.y);
21366         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21367         return oCoord;
21368     },
21369
21370     /**
21371      * Saves the most recent position so that we can reset the constraints and
21372      * tick marks on-demand.  We need to know this so that we can calculate the
21373      * number of pixels the element is offset from its original position.
21374      * @method cachePosition
21375      * @param iPageX the current x position (optional, this just makes it so we
21376      * don't have to look it up again)
21377      * @param iPageY the current y position (optional, this just makes it so we
21378      * don't have to look it up again)
21379      */
21380     cachePosition: function(iPageX, iPageY) {
21381         if (iPageX) {
21382             this.lastPageX = iPageX;
21383             this.lastPageY = iPageY;
21384         } else {
21385             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21386             this.lastPageX = aCoord[0];
21387             this.lastPageY = aCoord[1];
21388         }
21389     },
21390
21391     /**
21392      * Auto-scroll the window if the dragged object has been moved beyond the
21393      * visible window boundary.
21394      * @method autoScroll
21395      * @param {int} x the drag element's x position
21396      * @param {int} y the drag element's y position
21397      * @param {int} h the height of the drag element
21398      * @param {int} w the width of the drag element
21399      * @private
21400      */
21401     autoScroll: function(x, y, h, w) {
21402
21403         if (this.scroll) {
21404             // The client height
21405             var clientH = Roo.lib.Dom.getViewWidth();
21406
21407             // The client width
21408             var clientW = Roo.lib.Dom.getViewHeight();
21409
21410             // The amt scrolled down
21411             var st = this.DDM.getScrollTop();
21412
21413             // The amt scrolled right
21414             var sl = this.DDM.getScrollLeft();
21415
21416             // Location of the bottom of the element
21417             var bot = h + y;
21418
21419             // Location of the right of the element
21420             var right = w + x;
21421
21422             // The distance from the cursor to the bottom of the visible area,
21423             // adjusted so that we don't scroll if the cursor is beyond the
21424             // element drag constraints
21425             var toBot = (clientH + st - y - this.deltaY);
21426
21427             // The distance from the cursor to the right of the visible area
21428             var toRight = (clientW + sl - x - this.deltaX);
21429
21430
21431             // How close to the edge the cursor must be before we scroll
21432             // var thresh = (document.all) ? 100 : 40;
21433             var thresh = 40;
21434
21435             // How many pixels to scroll per autoscroll op.  This helps to reduce
21436             // clunky scrolling. IE is more sensitive about this ... it needs this
21437             // value to be higher.
21438             var scrAmt = (document.all) ? 80 : 30;
21439
21440             // Scroll down if we are near the bottom of the visible page and the
21441             // obj extends below the crease
21442             if ( bot > clientH && toBot < thresh ) {
21443                 window.scrollTo(sl, st + scrAmt);
21444             }
21445
21446             // Scroll up if the window is scrolled down and the top of the object
21447             // goes above the top border
21448             if ( y < st && st > 0 && y - st < thresh ) {
21449                 window.scrollTo(sl, st - scrAmt);
21450             }
21451
21452             // Scroll right if the obj is beyond the right border and the cursor is
21453             // near the border.
21454             if ( right > clientW && toRight < thresh ) {
21455                 window.scrollTo(sl + scrAmt, st);
21456             }
21457
21458             // Scroll left if the window has been scrolled to the right and the obj
21459             // extends past the left border
21460             if ( x < sl && sl > 0 && x - sl < thresh ) {
21461                 window.scrollTo(sl - scrAmt, st);
21462             }
21463         }
21464     },
21465
21466     /**
21467      * Finds the location the element should be placed if we want to move
21468      * it to where the mouse location less the click offset would place us.
21469      * @method getTargetCoord
21470      * @param {int} iPageX the X coordinate of the click
21471      * @param {int} iPageY the Y coordinate of the click
21472      * @return an object that contains the coordinates (Object.x and Object.y)
21473      * @private
21474      */
21475     getTargetCoord: function(iPageX, iPageY) {
21476
21477
21478         var x = iPageX - this.deltaX;
21479         var y = iPageY - this.deltaY;
21480
21481         if (this.constrainX) {
21482             if (x < this.minX) { x = this.minX; }
21483             if (x > this.maxX) { x = this.maxX; }
21484         }
21485
21486         if (this.constrainY) {
21487             if (y < this.minY) { y = this.minY; }
21488             if (y > this.maxY) { y = this.maxY; }
21489         }
21490
21491         x = this.getTick(x, this.xTicks);
21492         y = this.getTick(y, this.yTicks);
21493
21494
21495         return {x:x, y:y};
21496     },
21497
21498     /*
21499      * Sets up config options specific to this class. Overrides
21500      * Roo.dd.DragDrop, but all versions of this method through the
21501      * inheritance chain are called
21502      */
21503     applyConfig: function() {
21504         Roo.dd.DD.superclass.applyConfig.call(this);
21505         this.scroll = (this.config.scroll !== false);
21506     },
21507
21508     /*
21509      * Event that fires prior to the onMouseDown event.  Overrides
21510      * Roo.dd.DragDrop.
21511      */
21512     b4MouseDown: function(e) {
21513         // this.resetConstraints();
21514         this.autoOffset(e.getPageX(),
21515                             e.getPageY());
21516     },
21517
21518     /*
21519      * Event that fires prior to the onDrag event.  Overrides
21520      * Roo.dd.DragDrop.
21521      */
21522     b4Drag: function(e) {
21523         this.setDragElPos(e.getPageX(),
21524                             e.getPageY());
21525     },
21526
21527     toString: function() {
21528         return ("DD " + this.id);
21529     }
21530
21531     //////////////////////////////////////////////////////////////////////////
21532     // Debugging ygDragDrop events that can be overridden
21533     //////////////////////////////////////////////////////////////////////////
21534     /*
21535     startDrag: function(x, y) {
21536     },
21537
21538     onDrag: function(e) {
21539     },
21540
21541     onDragEnter: function(e, id) {
21542     },
21543
21544     onDragOver: function(e, id) {
21545     },
21546
21547     onDragOut: function(e, id) {
21548     },
21549
21550     onDragDrop: function(e, id) {
21551     },
21552
21553     endDrag: function(e) {
21554     }
21555
21556     */
21557
21558 });/*
21559  * Based on:
21560  * Ext JS Library 1.1.1
21561  * Copyright(c) 2006-2007, Ext JS, LLC.
21562  *
21563  * Originally Released Under LGPL - original licence link has changed is not relivant.
21564  *
21565  * Fork - LGPL
21566  * <script type="text/javascript">
21567  */
21568
21569 /**
21570  * @class Roo.dd.DDProxy
21571  * A DragDrop implementation that inserts an empty, bordered div into
21572  * the document that follows the cursor during drag operations.  At the time of
21573  * the click, the frame div is resized to the dimensions of the linked html
21574  * element, and moved to the exact location of the linked element.
21575  *
21576  * References to the "frame" element refer to the single proxy element that
21577  * was created to be dragged in place of all DDProxy elements on the
21578  * page.
21579  *
21580  * @extends Roo.dd.DD
21581  * @constructor
21582  * @param {String} id the id of the linked html element
21583  * @param {String} sGroup the group of related DragDrop objects
21584  * @param {object} config an object containing configurable attributes
21585  *                Valid properties for DDProxy in addition to those in DragDrop:
21586  *                   resizeFrame, centerFrame, dragElId
21587  */
21588 Roo.dd.DDProxy = function(id, sGroup, config) {
21589     if (id) {
21590         this.init(id, sGroup, config);
21591         this.initFrame();
21592     }
21593 };
21594
21595 /**
21596  * The default drag frame div id
21597  * @property Roo.dd.DDProxy.dragElId
21598  * @type String
21599  * @static
21600  */
21601 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21602
21603 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21604
21605     /**
21606      * By default we resize the drag frame to be the same size as the element
21607      * we want to drag (this is to get the frame effect).  We can turn it off
21608      * if we want a different behavior.
21609      * @property resizeFrame
21610      * @type boolean
21611      */
21612     resizeFrame: true,
21613
21614     /**
21615      * By default the frame is positioned exactly where the drag element is, so
21616      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21617      * you do not have constraints on the obj is to have the drag frame centered
21618      * around the cursor.  Set centerFrame to true for this effect.
21619      * @property centerFrame
21620      * @type boolean
21621      */
21622     centerFrame: false,
21623
21624     /**
21625      * Creates the proxy element if it does not yet exist
21626      * @method createFrame
21627      */
21628     createFrame: function() {
21629         var self = this;
21630         var body = document.body;
21631
21632         if (!body || !body.firstChild) {
21633             setTimeout( function() { self.createFrame(); }, 50 );
21634             return;
21635         }
21636
21637         var div = this.getDragEl();
21638
21639         if (!div) {
21640             div    = document.createElement("div");
21641             div.id = this.dragElId;
21642             var s  = div.style;
21643
21644             s.position   = "absolute";
21645             s.visibility = "hidden";
21646             s.cursor     = "move";
21647             s.border     = "2px solid #aaa";
21648             s.zIndex     = 999;
21649
21650             // appendChild can blow up IE if invoked prior to the window load event
21651             // while rendering a table.  It is possible there are other scenarios
21652             // that would cause this to happen as well.
21653             body.insertBefore(div, body.firstChild);
21654         }
21655     },
21656
21657     /**
21658      * Initialization for the drag frame element.  Must be called in the
21659      * constructor of all subclasses
21660      * @method initFrame
21661      */
21662     initFrame: function() {
21663         this.createFrame();
21664     },
21665
21666     applyConfig: function() {
21667         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21668
21669         this.resizeFrame = (this.config.resizeFrame !== false);
21670         this.centerFrame = (this.config.centerFrame);
21671         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21672     },
21673
21674     /**
21675      * Resizes the drag frame to the dimensions of the clicked object, positions
21676      * it over the object, and finally displays it
21677      * @method showFrame
21678      * @param {int} iPageX X click position
21679      * @param {int} iPageY Y click position
21680      * @private
21681      */
21682     showFrame: function(iPageX, iPageY) {
21683         var el = this.getEl();
21684         var dragEl = this.getDragEl();
21685         var s = dragEl.style;
21686
21687         this._resizeProxy();
21688
21689         if (this.centerFrame) {
21690             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21691                            Math.round(parseInt(s.height, 10)/2) );
21692         }
21693
21694         this.setDragElPos(iPageX, iPageY);
21695
21696         Roo.fly(dragEl).show();
21697     },
21698
21699     /**
21700      * The proxy is automatically resized to the dimensions of the linked
21701      * element when a drag is initiated, unless resizeFrame is set to false
21702      * @method _resizeProxy
21703      * @private
21704      */
21705     _resizeProxy: function() {
21706         if (this.resizeFrame) {
21707             var el = this.getEl();
21708             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21709         }
21710     },
21711
21712     // overrides Roo.dd.DragDrop
21713     b4MouseDown: function(e) {
21714         var x = e.getPageX();
21715         var y = e.getPageY();
21716         this.autoOffset(x, y);
21717         this.setDragElPos(x, y);
21718     },
21719
21720     // overrides Roo.dd.DragDrop
21721     b4StartDrag: function(x, y) {
21722         // show the drag frame
21723         this.showFrame(x, y);
21724     },
21725
21726     // overrides Roo.dd.DragDrop
21727     b4EndDrag: function(e) {
21728         Roo.fly(this.getDragEl()).hide();
21729     },
21730
21731     // overrides Roo.dd.DragDrop
21732     // By default we try to move the element to the last location of the frame.
21733     // This is so that the default behavior mirrors that of Roo.dd.DD.
21734     endDrag: function(e) {
21735
21736         var lel = this.getEl();
21737         var del = this.getDragEl();
21738
21739         // Show the drag frame briefly so we can get its position
21740         del.style.visibility = "";
21741
21742         this.beforeMove();
21743         // Hide the linked element before the move to get around a Safari
21744         // rendering bug.
21745         lel.style.visibility = "hidden";
21746         Roo.dd.DDM.moveToEl(lel, del);
21747         del.style.visibility = "hidden";
21748         lel.style.visibility = "";
21749
21750         this.afterDrag();
21751     },
21752
21753     beforeMove : function(){
21754
21755     },
21756
21757     afterDrag : function(){
21758
21759     },
21760
21761     toString: function() {
21762         return ("DDProxy " + this.id);
21763     }
21764
21765 });
21766 /*
21767  * Based on:
21768  * Ext JS Library 1.1.1
21769  * Copyright(c) 2006-2007, Ext JS, LLC.
21770  *
21771  * Originally Released Under LGPL - original licence link has changed is not relivant.
21772  *
21773  * Fork - LGPL
21774  * <script type="text/javascript">
21775  */
21776
21777  /**
21778  * @class Roo.dd.DDTarget
21779  * A DragDrop implementation that does not move, but can be a drop
21780  * target.  You would get the same result by simply omitting implementation
21781  * for the event callbacks, but this way we reduce the processing cost of the
21782  * event listener and the callbacks.
21783  * @extends Roo.dd.DragDrop
21784  * @constructor
21785  * @param {String} id the id of the element that is a drop target
21786  * @param {String} sGroup the group of related DragDrop objects
21787  * @param {object} config an object containing configurable attributes
21788  *                 Valid properties for DDTarget in addition to those in
21789  *                 DragDrop:
21790  *                    none
21791  */
21792 Roo.dd.DDTarget = function(id, sGroup, config) {
21793     if (id) {
21794         this.initTarget(id, sGroup, config);
21795     }
21796     if (config && (config.listeners || config.events)) { 
21797         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21798             listeners : config.listeners || {}, 
21799             events : config.events || {} 
21800         });    
21801     }
21802 };
21803
21804 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21805 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21806     toString: function() {
21807         return ("DDTarget " + this.id);
21808     }
21809 });
21810 /*
21811  * Based on:
21812  * Ext JS Library 1.1.1
21813  * Copyright(c) 2006-2007, Ext JS, LLC.
21814  *
21815  * Originally Released Under LGPL - original licence link has changed is not relivant.
21816  *
21817  * Fork - LGPL
21818  * <script type="text/javascript">
21819  */
21820  
21821
21822 /**
21823  * @class Roo.dd.ScrollManager
21824  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21825  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21826  * @singleton
21827  */
21828 Roo.dd.ScrollManager = function(){
21829     var ddm = Roo.dd.DragDropMgr;
21830     var els = {};
21831     var dragEl = null;
21832     var proc = {};
21833     
21834     
21835     
21836     var onStop = function(e){
21837         dragEl = null;
21838         clearProc();
21839     };
21840     
21841     var triggerRefresh = function(){
21842         if(ddm.dragCurrent){
21843              ddm.refreshCache(ddm.dragCurrent.groups);
21844         }
21845     };
21846     
21847     var doScroll = function(){
21848         if(ddm.dragCurrent){
21849             var dds = Roo.dd.ScrollManager;
21850             if(!dds.animate){
21851                 if(proc.el.scroll(proc.dir, dds.increment)){
21852                     triggerRefresh();
21853                 }
21854             }else{
21855                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21856             }
21857         }
21858     };
21859     
21860     var clearProc = function(){
21861         if(proc.id){
21862             clearInterval(proc.id);
21863         }
21864         proc.id = 0;
21865         proc.el = null;
21866         proc.dir = "";
21867     };
21868     
21869     var startProc = function(el, dir){
21870          Roo.log('scroll startproc');
21871         clearProc();
21872         proc.el = el;
21873         proc.dir = dir;
21874         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21875     };
21876     
21877     var onFire = function(e, isDrop){
21878        
21879         if(isDrop || !ddm.dragCurrent){ return; }
21880         var dds = Roo.dd.ScrollManager;
21881         if(!dragEl || dragEl != ddm.dragCurrent){
21882             dragEl = ddm.dragCurrent;
21883             // refresh regions on drag start
21884             dds.refreshCache();
21885         }
21886         
21887         var xy = Roo.lib.Event.getXY(e);
21888         var pt = new Roo.lib.Point(xy[0], xy[1]);
21889         for(var id in els){
21890             var el = els[id], r = el._region;
21891             if(r && r.contains(pt) && el.isScrollable()){
21892                 if(r.bottom - pt.y <= dds.thresh){
21893                     if(proc.el != el){
21894                         startProc(el, "down");
21895                     }
21896                     return;
21897                 }else if(r.right - pt.x <= dds.thresh){
21898                     if(proc.el != el){
21899                         startProc(el, "left");
21900                     }
21901                     return;
21902                 }else if(pt.y - r.top <= dds.thresh){
21903                     if(proc.el != el){
21904                         startProc(el, "up");
21905                     }
21906                     return;
21907                 }else if(pt.x - r.left <= dds.thresh){
21908                     if(proc.el != el){
21909                         startProc(el, "right");
21910                     }
21911                     return;
21912                 }
21913             }
21914         }
21915         clearProc();
21916     };
21917     
21918     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21919     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21920     
21921     return {
21922         /**
21923          * Registers new overflow element(s) to auto scroll
21924          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21925          */
21926         register : function(el){
21927             if(el instanceof Array){
21928                 for(var i = 0, len = el.length; i < len; i++) {
21929                         this.register(el[i]);
21930                 }
21931             }else{
21932                 el = Roo.get(el);
21933                 els[el.id] = el;
21934             }
21935             Roo.dd.ScrollManager.els = els;
21936         },
21937         
21938         /**
21939          * Unregisters overflow element(s) so they are no longer scrolled
21940          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21941          */
21942         unregister : function(el){
21943             if(el instanceof Array){
21944                 for(var i = 0, len = el.length; i < len; i++) {
21945                         this.unregister(el[i]);
21946                 }
21947             }else{
21948                 el = Roo.get(el);
21949                 delete els[el.id];
21950             }
21951         },
21952         
21953         /**
21954          * The number of pixels from the edge of a container the pointer needs to be to 
21955          * trigger scrolling (defaults to 25)
21956          * @type Number
21957          */
21958         thresh : 25,
21959         
21960         /**
21961          * The number of pixels to scroll in each scroll increment (defaults to 50)
21962          * @type Number
21963          */
21964         increment : 100,
21965         
21966         /**
21967          * The frequency of scrolls in milliseconds (defaults to 500)
21968          * @type Number
21969          */
21970         frequency : 500,
21971         
21972         /**
21973          * True to animate the scroll (defaults to true)
21974          * @type Boolean
21975          */
21976         animate: true,
21977         
21978         /**
21979          * The animation duration in seconds - 
21980          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21981          * @type Number
21982          */
21983         animDuration: .4,
21984         
21985         /**
21986          * Manually trigger a cache refresh.
21987          */
21988         refreshCache : function(){
21989             for(var id in els){
21990                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21991                     els[id]._region = els[id].getRegion();
21992                 }
21993             }
21994         }
21995     };
21996 }();/*
21997  * Based on:
21998  * Ext JS Library 1.1.1
21999  * Copyright(c) 2006-2007, Ext JS, LLC.
22000  *
22001  * Originally Released Under LGPL - original licence link has changed is not relivant.
22002  *
22003  * Fork - LGPL
22004  * <script type="text/javascript">
22005  */
22006  
22007
22008 /**
22009  * @class Roo.dd.Registry
22010  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22011  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22012  * @singleton
22013  */
22014 Roo.dd.Registry = function(){
22015     var elements = {}; 
22016     var handles = {}; 
22017     var autoIdSeed = 0;
22018
22019     var getId = function(el, autogen){
22020         if(typeof el == "string"){
22021             return el;
22022         }
22023         var id = el.id;
22024         if(!id && autogen !== false){
22025             id = "roodd-" + (++autoIdSeed);
22026             el.id = id;
22027         }
22028         return id;
22029     };
22030     
22031     return {
22032     /**
22033      * Register a drag drop element
22034      * @param {String|HTMLElement} element The id or DOM node to register
22035      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22036      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22037      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22038      * populated in the data object (if applicable):
22039      * <pre>
22040 Value      Description<br />
22041 ---------  ------------------------------------------<br />
22042 handles    Array of DOM nodes that trigger dragging<br />
22043            for the element being registered<br />
22044 isHandle   True if the element passed in triggers<br />
22045            dragging itself, else false
22046 </pre>
22047      */
22048         register : function(el, data){
22049             data = data || {};
22050             if(typeof el == "string"){
22051                 el = document.getElementById(el);
22052             }
22053             data.ddel = el;
22054             elements[getId(el)] = data;
22055             if(data.isHandle !== false){
22056                 handles[data.ddel.id] = data;
22057             }
22058             if(data.handles){
22059                 var hs = data.handles;
22060                 for(var i = 0, len = hs.length; i < len; i++){
22061                         handles[getId(hs[i])] = data;
22062                 }
22063             }
22064         },
22065
22066     /**
22067      * Unregister a drag drop element
22068      * @param {String|HTMLElement}  element The id or DOM node to unregister
22069      */
22070         unregister : function(el){
22071             var id = getId(el, false);
22072             var data = elements[id];
22073             if(data){
22074                 delete elements[id];
22075                 if(data.handles){
22076                     var hs = data.handles;
22077                     for(var i = 0, len = hs.length; i < len; i++){
22078                         delete handles[getId(hs[i], false)];
22079                     }
22080                 }
22081             }
22082         },
22083
22084     /**
22085      * Returns the handle registered for a DOM Node by id
22086      * @param {String|HTMLElement} id The DOM node or id to look up
22087      * @return {Object} handle The custom handle data
22088      */
22089         getHandle : function(id){
22090             if(typeof id != "string"){ // must be element?
22091                 id = id.id;
22092             }
22093             return handles[id];
22094         },
22095
22096     /**
22097      * Returns the handle that is registered for the DOM node that is the target of the event
22098      * @param {Event} e The event
22099      * @return {Object} handle The custom handle data
22100      */
22101         getHandleFromEvent : function(e){
22102             var t = Roo.lib.Event.getTarget(e);
22103             return t ? handles[t.id] : null;
22104         },
22105
22106     /**
22107      * Returns a custom data object that is registered for a DOM node by id
22108      * @param {String|HTMLElement} id The DOM node or id to look up
22109      * @return {Object} data The custom data
22110      */
22111         getTarget : function(id){
22112             if(typeof id != "string"){ // must be element?
22113                 id = id.id;
22114             }
22115             return elements[id];
22116         },
22117
22118     /**
22119      * Returns a custom data object that is registered for the DOM node that is the target of the event
22120      * @param {Event} e The event
22121      * @return {Object} data The custom data
22122      */
22123         getTargetFromEvent : function(e){
22124             var t = Roo.lib.Event.getTarget(e);
22125             return t ? elements[t.id] || handles[t.id] : null;
22126         }
22127     };
22128 }();/*
22129  * Based on:
22130  * Ext JS Library 1.1.1
22131  * Copyright(c) 2006-2007, Ext JS, LLC.
22132  *
22133  * Originally Released Under LGPL - original licence link has changed is not relivant.
22134  *
22135  * Fork - LGPL
22136  * <script type="text/javascript">
22137  */
22138  
22139
22140 /**
22141  * @class Roo.dd.StatusProxy
22142  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22143  * default drag proxy used by all Roo.dd components.
22144  * @constructor
22145  * @param {Object} config
22146  */
22147 Roo.dd.StatusProxy = function(config){
22148     Roo.apply(this, config);
22149     this.id = this.id || Roo.id();
22150     this.el = new Roo.Layer({
22151         dh: {
22152             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22153                 {tag: "div", cls: "x-dd-drop-icon"},
22154                 {tag: "div", cls: "x-dd-drag-ghost"}
22155             ]
22156         }, 
22157         shadow: !config || config.shadow !== false
22158     });
22159     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22160     this.dropStatus = this.dropNotAllowed;
22161 };
22162
22163 Roo.dd.StatusProxy.prototype = {
22164     /**
22165      * @cfg {String} dropAllowed
22166      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22167      */
22168     dropAllowed : "x-dd-drop-ok",
22169     /**
22170      * @cfg {String} dropNotAllowed
22171      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22172      */
22173     dropNotAllowed : "x-dd-drop-nodrop",
22174
22175     /**
22176      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22177      * over the current target element.
22178      * @param {String} cssClass The css class for the new drop status indicator image
22179      */
22180     setStatus : function(cssClass){
22181         cssClass = cssClass || this.dropNotAllowed;
22182         if(this.dropStatus != cssClass){
22183             this.el.replaceClass(this.dropStatus, cssClass);
22184             this.dropStatus = cssClass;
22185         }
22186     },
22187
22188     /**
22189      * Resets the status indicator to the default dropNotAllowed value
22190      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22191      */
22192     reset : function(clearGhost){
22193         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22194         this.dropStatus = this.dropNotAllowed;
22195         if(clearGhost){
22196             this.ghost.update("");
22197         }
22198     },
22199
22200     /**
22201      * Updates the contents of the ghost element
22202      * @param {String} html The html that will replace the current innerHTML of the ghost element
22203      */
22204     update : function(html){
22205         if(typeof html == "string"){
22206             this.ghost.update(html);
22207         }else{
22208             this.ghost.update("");
22209             html.style.margin = "0";
22210             this.ghost.dom.appendChild(html);
22211         }
22212         // ensure float = none set?? cant remember why though.
22213         var el = this.ghost.dom.firstChild;
22214                 if(el){
22215                         Roo.fly(el).setStyle('float', 'none');
22216                 }
22217     },
22218     
22219     /**
22220      * Returns the underlying proxy {@link Roo.Layer}
22221      * @return {Roo.Layer} el
22222     */
22223     getEl : function(){
22224         return this.el;
22225     },
22226
22227     /**
22228      * Returns the ghost element
22229      * @return {Roo.Element} el
22230      */
22231     getGhost : function(){
22232         return this.ghost;
22233     },
22234
22235     /**
22236      * Hides the proxy
22237      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22238      */
22239     hide : function(clear){
22240         this.el.hide();
22241         if(clear){
22242             this.reset(true);
22243         }
22244     },
22245
22246     /**
22247      * Stops the repair animation if it's currently running
22248      */
22249     stop : function(){
22250         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22251             this.anim.stop();
22252         }
22253     },
22254
22255     /**
22256      * Displays this proxy
22257      */
22258     show : function(){
22259         this.el.show();
22260     },
22261
22262     /**
22263      * Force the Layer to sync its shadow and shim positions to the element
22264      */
22265     sync : function(){
22266         this.el.sync();
22267     },
22268
22269     /**
22270      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22271      * invalid drop operation by the item being dragged.
22272      * @param {Array} xy The XY position of the element ([x, y])
22273      * @param {Function} callback The function to call after the repair is complete
22274      * @param {Object} scope The scope in which to execute the callback
22275      */
22276     repair : function(xy, callback, scope){
22277         this.callback = callback;
22278         this.scope = scope;
22279         if(xy && this.animRepair !== false){
22280             this.el.addClass("x-dd-drag-repair");
22281             this.el.hideUnders(true);
22282             this.anim = this.el.shift({
22283                 duration: this.repairDuration || .5,
22284                 easing: 'easeOut',
22285                 xy: xy,
22286                 stopFx: true,
22287                 callback: this.afterRepair,
22288                 scope: this
22289             });
22290         }else{
22291             this.afterRepair();
22292         }
22293     },
22294
22295     // private
22296     afterRepair : function(){
22297         this.hide(true);
22298         if(typeof this.callback == "function"){
22299             this.callback.call(this.scope || this);
22300         }
22301         this.callback = null;
22302         this.scope = null;
22303     }
22304 };/*
22305  * Based on:
22306  * Ext JS Library 1.1.1
22307  * Copyright(c) 2006-2007, Ext JS, LLC.
22308  *
22309  * Originally Released Under LGPL - original licence link has changed is not relivant.
22310  *
22311  * Fork - LGPL
22312  * <script type="text/javascript">
22313  */
22314
22315 /**
22316  * @class Roo.dd.DragSource
22317  * @extends Roo.dd.DDProxy
22318  * A simple class that provides the basic implementation needed to make any element draggable.
22319  * @constructor
22320  * @param {String/HTMLElement/Element} el The container element
22321  * @param {Object} config
22322  */
22323 Roo.dd.DragSource = function(el, config){
22324     this.el = Roo.get(el);
22325     this.dragData = {};
22326     
22327     Roo.apply(this, config);
22328     
22329     if(!this.proxy){
22330         this.proxy = new Roo.dd.StatusProxy();
22331     }
22332
22333     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22334           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22335     
22336     this.dragging = false;
22337 };
22338
22339 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22340     /**
22341      * @cfg {String} dropAllowed
22342      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22343      */
22344     dropAllowed : "x-dd-drop-ok",
22345     /**
22346      * @cfg {String} dropNotAllowed
22347      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22348      */
22349     dropNotAllowed : "x-dd-drop-nodrop",
22350
22351     /**
22352      * Returns the data object associated with this drag source
22353      * @return {Object} data An object containing arbitrary data
22354      */
22355     getDragData : function(e){
22356         return this.dragData;
22357     },
22358
22359     // private
22360     onDragEnter : function(e, id){
22361         var target = Roo.dd.DragDropMgr.getDDById(id);
22362         this.cachedTarget = target;
22363         if(this.beforeDragEnter(target, e, id) !== false){
22364             if(target.isNotifyTarget){
22365                 var status = target.notifyEnter(this, e, this.dragData);
22366                 this.proxy.setStatus(status);
22367             }else{
22368                 this.proxy.setStatus(this.dropAllowed);
22369             }
22370             
22371             if(this.afterDragEnter){
22372                 /**
22373                  * An empty function by default, but provided so that you can perform a custom action
22374                  * when the dragged item enters the drop target by providing an implementation.
22375                  * @param {Roo.dd.DragDrop} target The drop target
22376                  * @param {Event} e The event object
22377                  * @param {String} id The id of the dragged element
22378                  * @method afterDragEnter
22379                  */
22380                 this.afterDragEnter(target, e, id);
22381             }
22382         }
22383     },
22384
22385     /**
22386      * An empty function by default, but provided so that you can perform a custom action
22387      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22388      * @param {Roo.dd.DragDrop} target The drop target
22389      * @param {Event} e The event object
22390      * @param {String} id The id of the dragged element
22391      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22392      */
22393     beforeDragEnter : function(target, e, id){
22394         return true;
22395     },
22396
22397     // private
22398     alignElWithMouse: function() {
22399         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22400         this.proxy.sync();
22401     },
22402
22403     // private
22404     onDragOver : function(e, id){
22405         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22406         if(this.beforeDragOver(target, e, id) !== false){
22407             if(target.isNotifyTarget){
22408                 var status = target.notifyOver(this, e, this.dragData);
22409                 this.proxy.setStatus(status);
22410             }
22411
22412             if(this.afterDragOver){
22413                 /**
22414                  * An empty function by default, but provided so that you can perform a custom action
22415                  * while the dragged item is over the drop target by providing an implementation.
22416                  * @param {Roo.dd.DragDrop} target The drop target
22417                  * @param {Event} e The event object
22418                  * @param {String} id The id of the dragged element
22419                  * @method afterDragOver
22420                  */
22421                 this.afterDragOver(target, e, id);
22422             }
22423         }
22424     },
22425
22426     /**
22427      * An empty function by default, but provided so that you can perform a custom action
22428      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22429      * @param {Roo.dd.DragDrop} target The drop target
22430      * @param {Event} e The event object
22431      * @param {String} id The id of the dragged element
22432      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22433      */
22434     beforeDragOver : function(target, e, id){
22435         return true;
22436     },
22437
22438     // private
22439     onDragOut : function(e, id){
22440         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22441         if(this.beforeDragOut(target, e, id) !== false){
22442             if(target.isNotifyTarget){
22443                 target.notifyOut(this, e, this.dragData);
22444             }
22445             this.proxy.reset();
22446             if(this.afterDragOut){
22447                 /**
22448                  * An empty function by default, but provided so that you can perform a custom action
22449                  * after the dragged item is dragged out of the target without dropping.
22450                  * @param {Roo.dd.DragDrop} target The drop target
22451                  * @param {Event} e The event object
22452                  * @param {String} id The id of the dragged element
22453                  * @method afterDragOut
22454                  */
22455                 this.afterDragOut(target, e, id);
22456             }
22457         }
22458         this.cachedTarget = null;
22459     },
22460
22461     /**
22462      * An empty function by default, but provided so that you can perform a custom action before the dragged
22463      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22464      * @param {Roo.dd.DragDrop} target The drop target
22465      * @param {Event} e The event object
22466      * @param {String} id The id of the dragged element
22467      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22468      */
22469     beforeDragOut : function(target, e, id){
22470         return true;
22471     },
22472     
22473     // private
22474     onDragDrop : function(e, id){
22475         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22476         if(this.beforeDragDrop(target, e, id) !== false){
22477             if(target.isNotifyTarget){
22478                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22479                     this.onValidDrop(target, e, id);
22480                 }else{
22481                     this.onInvalidDrop(target, e, id);
22482                 }
22483             }else{
22484                 this.onValidDrop(target, e, id);
22485             }
22486             
22487             if(this.afterDragDrop){
22488                 /**
22489                  * An empty function by default, but provided so that you can perform a custom action
22490                  * after a valid drag drop has occurred by providing an implementation.
22491                  * @param {Roo.dd.DragDrop} target The drop target
22492                  * @param {Event} e The event object
22493                  * @param {String} id The id of the dropped element
22494                  * @method afterDragDrop
22495                  */
22496                 this.afterDragDrop(target, e, id);
22497             }
22498         }
22499         delete this.cachedTarget;
22500     },
22501
22502     /**
22503      * An empty function by default, but provided so that you can perform a custom action before the dragged
22504      * item is dropped onto the target and optionally cancel the onDragDrop.
22505      * @param {Roo.dd.DragDrop} target The drop target
22506      * @param {Event} e The event object
22507      * @param {String} id The id of the dragged element
22508      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22509      */
22510     beforeDragDrop : function(target, e, id){
22511         return true;
22512     },
22513
22514     // private
22515     onValidDrop : function(target, e, id){
22516         this.hideProxy();
22517         if(this.afterValidDrop){
22518             /**
22519              * An empty function by default, but provided so that you can perform a custom action
22520              * after a valid drop has occurred by providing an implementation.
22521              * @param {Object} target The target DD 
22522              * @param {Event} e The event object
22523              * @param {String} id The id of the dropped element
22524              * @method afterInvalidDrop
22525              */
22526             this.afterValidDrop(target, e, id);
22527         }
22528     },
22529
22530     // private
22531     getRepairXY : function(e, data){
22532         return this.el.getXY();  
22533     },
22534
22535     // private
22536     onInvalidDrop : function(target, e, id){
22537         this.beforeInvalidDrop(target, e, id);
22538         if(this.cachedTarget){
22539             if(this.cachedTarget.isNotifyTarget){
22540                 this.cachedTarget.notifyOut(this, e, this.dragData);
22541             }
22542             this.cacheTarget = null;
22543         }
22544         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22545
22546         if(this.afterInvalidDrop){
22547             /**
22548              * An empty function by default, but provided so that you can perform a custom action
22549              * after an invalid drop has occurred by providing an implementation.
22550              * @param {Event} e The event object
22551              * @param {String} id The id of the dropped element
22552              * @method afterInvalidDrop
22553              */
22554             this.afterInvalidDrop(e, id);
22555         }
22556     },
22557
22558     // private
22559     afterRepair : function(){
22560         if(Roo.enableFx){
22561             this.el.highlight(this.hlColor || "c3daf9");
22562         }
22563         this.dragging = false;
22564     },
22565
22566     /**
22567      * An empty function by default, but provided so that you can perform a custom action after an invalid
22568      * drop has occurred.
22569      * @param {Roo.dd.DragDrop} target The drop target
22570      * @param {Event} e The event object
22571      * @param {String} id The id of the dragged element
22572      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22573      */
22574     beforeInvalidDrop : function(target, e, id){
22575         return true;
22576     },
22577
22578     // private
22579     handleMouseDown : function(e){
22580         if(this.dragging) {
22581             return;
22582         }
22583         var data = this.getDragData(e);
22584         if(data && this.onBeforeDrag(data, e) !== false){
22585             this.dragData = data;
22586             this.proxy.stop();
22587             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22588         } 
22589     },
22590
22591     /**
22592      * An empty function by default, but provided so that you can perform a custom action before the initial
22593      * drag event begins and optionally cancel it.
22594      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22595      * @param {Event} e The event object
22596      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22597      */
22598     onBeforeDrag : function(data, e){
22599         return true;
22600     },
22601
22602     /**
22603      * An empty function by default, but provided so that you can perform a custom action once the initial
22604      * drag event has begun.  The drag cannot be canceled from this function.
22605      * @param {Number} x The x position of the click on the dragged object
22606      * @param {Number} y The y position of the click on the dragged object
22607      */
22608     onStartDrag : Roo.emptyFn,
22609
22610     // private - YUI override
22611     startDrag : function(x, y){
22612         this.proxy.reset();
22613         this.dragging = true;
22614         this.proxy.update("");
22615         this.onInitDrag(x, y);
22616         this.proxy.show();
22617     },
22618
22619     // private
22620     onInitDrag : function(x, y){
22621         var clone = this.el.dom.cloneNode(true);
22622         clone.id = Roo.id(); // prevent duplicate ids
22623         this.proxy.update(clone);
22624         this.onStartDrag(x, y);
22625         return true;
22626     },
22627
22628     /**
22629      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22630      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22631      */
22632     getProxy : function(){
22633         return this.proxy;  
22634     },
22635
22636     /**
22637      * Hides the drag source's {@link Roo.dd.StatusProxy}
22638      */
22639     hideProxy : function(){
22640         this.proxy.hide();  
22641         this.proxy.reset(true);
22642         this.dragging = false;
22643     },
22644
22645     // private
22646     triggerCacheRefresh : function(){
22647         Roo.dd.DDM.refreshCache(this.groups);
22648     },
22649
22650     // private - override to prevent hiding
22651     b4EndDrag: function(e) {
22652     },
22653
22654     // private - override to prevent moving
22655     endDrag : function(e){
22656         this.onEndDrag(this.dragData, e);
22657     },
22658
22659     // private
22660     onEndDrag : function(data, e){
22661     },
22662     
22663     // private - pin to cursor
22664     autoOffset : function(x, y) {
22665         this.setDelta(-12, -20);
22666     }    
22667 });/*
22668  * Based on:
22669  * Ext JS Library 1.1.1
22670  * Copyright(c) 2006-2007, Ext JS, LLC.
22671  *
22672  * Originally Released Under LGPL - original licence link has changed is not relivant.
22673  *
22674  * Fork - LGPL
22675  * <script type="text/javascript">
22676  */
22677
22678
22679 /**
22680  * @class Roo.dd.DropTarget
22681  * @extends Roo.dd.DDTarget
22682  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22683  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22684  * @constructor
22685  * @param {String/HTMLElement/Element} el The container element
22686  * @param {Object} config
22687  */
22688 Roo.dd.DropTarget = function(el, config){
22689     this.el = Roo.get(el);
22690     
22691     var listeners = false; ;
22692     if (config && config.listeners) {
22693         listeners= config.listeners;
22694         delete config.listeners;
22695     }
22696     Roo.apply(this, config);
22697     
22698     if(this.containerScroll){
22699         Roo.dd.ScrollManager.register(this.el);
22700     }
22701     this.addEvents( {
22702          /**
22703          * @scope Roo.dd.DropTarget
22704          */
22705          
22706          /**
22707          * @event enter
22708          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22709          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22710          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22711          * 
22712          * IMPORTANT : it should set  this.valid to true|false
22713          * 
22714          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22715          * @param {Event} e The event
22716          * @param {Object} data An object containing arbitrary data supplied by the drag source
22717          */
22718         "enter" : true,
22719         
22720          /**
22721          * @event over
22722          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22723          * This method will be called on every mouse movement while the drag source is over the drop target.
22724          * This default implementation simply returns the dropAllowed config value.
22725          * 
22726          * IMPORTANT : it should set  this.valid to true|false
22727          * 
22728          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22729          * @param {Event} e The event
22730          * @param {Object} data An object containing arbitrary data supplied by the drag source
22731          
22732          */
22733         "over" : true,
22734         /**
22735          * @event out
22736          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22737          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22738          * overClass (if any) from the drop element.
22739          * 
22740          * 
22741          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22742          * @param {Event} e The event
22743          * @param {Object} data An object containing arbitrary data supplied by the drag source
22744          */
22745          "out" : true,
22746          
22747         /**
22748          * @event drop
22749          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22750          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22751          * implementation that does something to process the drop event and returns true so that the drag source's
22752          * repair action does not run.
22753          * 
22754          * IMPORTANT : it should set this.success
22755          * 
22756          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22757          * @param {Event} e The event
22758          * @param {Object} data An object containing arbitrary data supplied by the drag source
22759         */
22760          "drop" : true
22761     });
22762             
22763      
22764     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22765         this.el.dom, 
22766         this.ddGroup || this.group,
22767         {
22768             isTarget: true,
22769             listeners : listeners || {} 
22770            
22771         
22772         }
22773     );
22774
22775 };
22776
22777 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22778     /**
22779      * @cfg {String} overClass
22780      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22781      */
22782      /**
22783      * @cfg {String} ddGroup
22784      * The drag drop group to handle drop events for
22785      */
22786      
22787     /**
22788      * @cfg {String} dropAllowed
22789      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22790      */
22791     dropAllowed : "x-dd-drop-ok",
22792     /**
22793      * @cfg {String} dropNotAllowed
22794      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22795      */
22796     dropNotAllowed : "x-dd-drop-nodrop",
22797     /**
22798      * @cfg {boolean} success
22799      * set this after drop listener.. 
22800      */
22801     success : false,
22802     /**
22803      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22804      * if the drop point is valid for over/enter..
22805      */
22806     valid : false,
22807     // private
22808     isTarget : true,
22809
22810     // private
22811     isNotifyTarget : true,
22812     
22813     /**
22814      * @hide
22815      */
22816     notifyEnter : function(dd, e, data)
22817     {
22818         this.valid = true;
22819         this.fireEvent('enter', dd, e, data);
22820         if(this.overClass){
22821             this.el.addClass(this.overClass);
22822         }
22823         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22824             this.valid ? this.dropAllowed : this.dropNotAllowed
22825         );
22826     },
22827
22828     /**
22829      * @hide
22830      */
22831     notifyOver : function(dd, e, data)
22832     {
22833         this.valid = true;
22834         this.fireEvent('over', dd, e, data);
22835         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22836             this.valid ? this.dropAllowed : this.dropNotAllowed
22837         );
22838     },
22839
22840     /**
22841      * @hide
22842      */
22843     notifyOut : function(dd, e, data)
22844     {
22845         this.fireEvent('out', dd, e, data);
22846         if(this.overClass){
22847             this.el.removeClass(this.overClass);
22848         }
22849     },
22850
22851     /**
22852      * @hide
22853      */
22854     notifyDrop : function(dd, e, data)
22855     {
22856         this.success = false;
22857         this.fireEvent('drop', dd, e, data);
22858         return this.success;
22859     }
22860 });/*
22861  * Based on:
22862  * Ext JS Library 1.1.1
22863  * Copyright(c) 2006-2007, Ext JS, LLC.
22864  *
22865  * Originally Released Under LGPL - original licence link has changed is not relivant.
22866  *
22867  * Fork - LGPL
22868  * <script type="text/javascript">
22869  */
22870
22871
22872 /**
22873  * @class Roo.dd.DragZone
22874  * @extends Roo.dd.DragSource
22875  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22876  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22877  * @constructor
22878  * @param {String/HTMLElement/Element} el The container element
22879  * @param {Object} config
22880  */
22881 Roo.dd.DragZone = function(el, config){
22882     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22883     if(this.containerScroll){
22884         Roo.dd.ScrollManager.register(this.el);
22885     }
22886 };
22887
22888 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22889     /**
22890      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22891      * for auto scrolling during drag operations.
22892      */
22893     /**
22894      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22895      * method after a failed drop (defaults to "c3daf9" - light blue)
22896      */
22897
22898     /**
22899      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22900      * for a valid target to drag based on the mouse down. Override this method
22901      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22902      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22903      * @param {EventObject} e The mouse down event
22904      * @return {Object} The dragData
22905      */
22906     getDragData : function(e){
22907         return Roo.dd.Registry.getHandleFromEvent(e);
22908     },
22909     
22910     /**
22911      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22912      * this.dragData.ddel
22913      * @param {Number} x The x position of the click on the dragged object
22914      * @param {Number} y The y position of the click on the dragged object
22915      * @return {Boolean} true to continue the drag, false to cancel
22916      */
22917     onInitDrag : function(x, y){
22918         this.proxy.update(this.dragData.ddel.cloneNode(true));
22919         this.onStartDrag(x, y);
22920         return true;
22921     },
22922     
22923     /**
22924      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22925      */
22926     afterRepair : function(){
22927         if(Roo.enableFx){
22928             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22929         }
22930         this.dragging = false;
22931     },
22932
22933     /**
22934      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22935      * the XY of this.dragData.ddel
22936      * @param {EventObject} e The mouse up event
22937      * @return {Array} The xy location (e.g. [100, 200])
22938      */
22939     getRepairXY : function(e){
22940         return Roo.Element.fly(this.dragData.ddel).getXY();  
22941     }
22942 });/*
22943  * Based on:
22944  * Ext JS Library 1.1.1
22945  * Copyright(c) 2006-2007, Ext JS, LLC.
22946  *
22947  * Originally Released Under LGPL - original licence link has changed is not relivant.
22948  *
22949  * Fork - LGPL
22950  * <script type="text/javascript">
22951  */
22952 /**
22953  * @class Roo.dd.DropZone
22954  * @extends Roo.dd.DropTarget
22955  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22956  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22957  * @constructor
22958  * @param {String/HTMLElement/Element} el The container element
22959  * @param {Object} config
22960  */
22961 Roo.dd.DropZone = function(el, config){
22962     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22963 };
22964
22965 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22966     /**
22967      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22968      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22969      * provide your own custom lookup.
22970      * @param {Event} e The event
22971      * @return {Object} data The custom data
22972      */
22973     getTargetFromEvent : function(e){
22974         return Roo.dd.Registry.getTargetFromEvent(e);
22975     },
22976
22977     /**
22978      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22979      * that it has registered.  This method has no default implementation and should be overridden to provide
22980      * node-specific processing if necessary.
22981      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22982      * {@link #getTargetFromEvent} for this node)
22983      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22984      * @param {Event} e The event
22985      * @param {Object} data An object containing arbitrary data supplied by the drag source
22986      */
22987     onNodeEnter : function(n, dd, e, data){
22988         
22989     },
22990
22991     /**
22992      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22993      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22994      * overridden to provide the proper feedback.
22995      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22996      * {@link #getTargetFromEvent} for this node)
22997      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22998      * @param {Event} e The event
22999      * @param {Object} data An object containing arbitrary data supplied by the drag source
23000      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23001      * underlying {@link Roo.dd.StatusProxy} can be updated
23002      */
23003     onNodeOver : function(n, dd, e, data){
23004         return this.dropAllowed;
23005     },
23006
23007     /**
23008      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23009      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23010      * node-specific processing if necessary.
23011      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23012      * {@link #getTargetFromEvent} for this node)
23013      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23014      * @param {Event} e The event
23015      * @param {Object} data An object containing arbitrary data supplied by the drag source
23016      */
23017     onNodeOut : function(n, dd, e, data){
23018         
23019     },
23020
23021     /**
23022      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23023      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23024      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23025      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23026      * {@link #getTargetFromEvent} for this node)
23027      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23028      * @param {Event} e The event
23029      * @param {Object} data An object containing arbitrary data supplied by the drag source
23030      * @return {Boolean} True if the drop was valid, else false
23031      */
23032     onNodeDrop : function(n, dd, e, data){
23033         return false;
23034     },
23035
23036     /**
23037      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23038      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23039      * it should be overridden to provide the proper feedback if necessary.
23040      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23041      * @param {Event} e The event
23042      * @param {Object} data An object containing arbitrary data supplied by the drag source
23043      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23044      * underlying {@link Roo.dd.StatusProxy} can be updated
23045      */
23046     onContainerOver : function(dd, e, data){
23047         return this.dropNotAllowed;
23048     },
23049
23050     /**
23051      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23052      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23053      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23054      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23055      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23056      * @param {Event} e The event
23057      * @param {Object} data An object containing arbitrary data supplied by the drag source
23058      * @return {Boolean} True if the drop was valid, else false
23059      */
23060     onContainerDrop : function(dd, e, data){
23061         return false;
23062     },
23063
23064     /**
23065      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23066      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23067      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23068      * you should override this method and provide a custom implementation.
23069      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23070      * @param {Event} e The event
23071      * @param {Object} data An object containing arbitrary data supplied by the drag source
23072      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23073      * underlying {@link Roo.dd.StatusProxy} can be updated
23074      */
23075     notifyEnter : function(dd, e, data){
23076         return this.dropNotAllowed;
23077     },
23078
23079     /**
23080      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23081      * This method will be called on every mouse movement while the drag source is over the drop zone.
23082      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23083      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23084      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23085      * registered node, it will call {@link #onContainerOver}.
23086      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23087      * @param {Event} e The event
23088      * @param {Object} data An object containing arbitrary data supplied by the drag source
23089      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23090      * underlying {@link Roo.dd.StatusProxy} can be updated
23091      */
23092     notifyOver : function(dd, e, data){
23093         var n = this.getTargetFromEvent(e);
23094         if(!n){ // not over valid drop target
23095             if(this.lastOverNode){
23096                 this.onNodeOut(this.lastOverNode, dd, e, data);
23097                 this.lastOverNode = null;
23098             }
23099             return this.onContainerOver(dd, e, data);
23100         }
23101         if(this.lastOverNode != n){
23102             if(this.lastOverNode){
23103                 this.onNodeOut(this.lastOverNode, dd, e, data);
23104             }
23105             this.onNodeEnter(n, dd, e, data);
23106             this.lastOverNode = n;
23107         }
23108         return this.onNodeOver(n, dd, e, data);
23109     },
23110
23111     /**
23112      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23113      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23114      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23116      * @param {Event} e The event
23117      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23118      */
23119     notifyOut : function(dd, e, data){
23120         if(this.lastOverNode){
23121             this.onNodeOut(this.lastOverNode, dd, e, data);
23122             this.lastOverNode = null;
23123         }
23124     },
23125
23126     /**
23127      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23128      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23129      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23130      * otherwise it will call {@link #onContainerDrop}.
23131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23132      * @param {Event} e The event
23133      * @param {Object} data An object containing arbitrary data supplied by the drag source
23134      * @return {Boolean} True if the drop was valid, else false
23135      */
23136     notifyDrop : function(dd, e, data){
23137         if(this.lastOverNode){
23138             this.onNodeOut(this.lastOverNode, dd, e, data);
23139             this.lastOverNode = null;
23140         }
23141         var n = this.getTargetFromEvent(e);
23142         return n ?
23143             this.onNodeDrop(n, dd, e, data) :
23144             this.onContainerDrop(dd, e, data);
23145     },
23146
23147     // private
23148     triggerCacheRefresh : function(){
23149         Roo.dd.DDM.refreshCache(this.groups);
23150     }  
23151 });/*
23152  * Based on:
23153  * Ext JS Library 1.1.1
23154  * Copyright(c) 2006-2007, Ext JS, LLC.
23155  *
23156  * Originally Released Under LGPL - original licence link has changed is not relivant.
23157  *
23158  * Fork - LGPL
23159  * <script type="text/javascript">
23160  */
23161
23162
23163 /**
23164  * @class Roo.data.SortTypes
23165  * @singleton
23166  * Defines the default sorting (casting?) comparison functions used when sorting data.
23167  */
23168 Roo.data.SortTypes = {
23169     /**
23170      * Default sort that does nothing
23171      * @param {Mixed} s The value being converted
23172      * @return {Mixed} The comparison value
23173      */
23174     none : function(s){
23175         return s;
23176     },
23177     
23178     /**
23179      * The regular expression used to strip tags
23180      * @type {RegExp}
23181      * @property
23182      */
23183     stripTagsRE : /<\/?[^>]+>/gi,
23184     
23185     /**
23186      * Strips all HTML tags to sort on text only
23187      * @param {Mixed} s The value being converted
23188      * @return {String} The comparison value
23189      */
23190     asText : function(s){
23191         return String(s).replace(this.stripTagsRE, "");
23192     },
23193     
23194     /**
23195      * Strips all HTML tags to sort on text only - Case insensitive
23196      * @param {Mixed} s The value being converted
23197      * @return {String} The comparison value
23198      */
23199     asUCText : function(s){
23200         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23201     },
23202     
23203     /**
23204      * Case insensitive string
23205      * @param {Mixed} s The value being converted
23206      * @return {String} The comparison value
23207      */
23208     asUCString : function(s) {
23209         return String(s).toUpperCase();
23210     },
23211     
23212     /**
23213      * Date sorting
23214      * @param {Mixed} s The value being converted
23215      * @return {Number} The comparison value
23216      */
23217     asDate : function(s) {
23218         if(!s){
23219             return 0;
23220         }
23221         if(s instanceof Date){
23222             return s.getTime();
23223         }
23224         return Date.parse(String(s));
23225     },
23226     
23227     /**
23228      * Float sorting
23229      * @param {Mixed} s The value being converted
23230      * @return {Float} The comparison value
23231      */
23232     asFloat : function(s) {
23233         var val = parseFloat(String(s).replace(/,/g, ""));
23234         if(isNaN(val)) {
23235             val = 0;
23236         }
23237         return val;
23238     },
23239     
23240     /**
23241      * Integer sorting
23242      * @param {Mixed} s The value being converted
23243      * @return {Number} The comparison value
23244      */
23245     asInt : function(s) {
23246         var val = parseInt(String(s).replace(/,/g, ""));
23247         if(isNaN(val)) {
23248             val = 0;
23249         }
23250         return val;
23251     }
23252 };/*
23253  * Based on:
23254  * Ext JS Library 1.1.1
23255  * Copyright(c) 2006-2007, Ext JS, LLC.
23256  *
23257  * Originally Released Under LGPL - original licence link has changed is not relivant.
23258  *
23259  * Fork - LGPL
23260  * <script type="text/javascript">
23261  */
23262
23263 /**
23264 * @class Roo.data.Record
23265  * Instances of this class encapsulate both record <em>definition</em> information, and record
23266  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23267  * to access Records cached in an {@link Roo.data.Store} object.<br>
23268  * <p>
23269  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23270  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23271  * objects.<br>
23272  * <p>
23273  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23274  * @constructor
23275  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23276  * {@link #create}. The parameters are the same.
23277  * @param {Array} data An associative Array of data values keyed by the field name.
23278  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23279  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23280  * not specified an integer id is generated.
23281  */
23282 Roo.data.Record = function(data, id){
23283     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23284     this.data = data;
23285 };
23286
23287 /**
23288  * Generate a constructor for a specific record layout.
23289  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23290  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23291  * Each field definition object may contain the following properties: <ul>
23292  * <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,
23293  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23294  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23295  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23296  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23297  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23298  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23299  * this may be omitted.</p></li>
23300  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23301  * <ul><li>auto (Default, implies no conversion)</li>
23302  * <li>string</li>
23303  * <li>int</li>
23304  * <li>float</li>
23305  * <li>boolean</li>
23306  * <li>date</li></ul></p></li>
23307  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23308  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23309  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23310  * by the Reader into an object that will be stored in the Record. It is passed the
23311  * following parameters:<ul>
23312  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23313  * </ul></p></li>
23314  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23315  * </ul>
23316  * <br>usage:<br><pre><code>
23317 var TopicRecord = Roo.data.Record.create(
23318     {name: 'title', mapping: 'topic_title'},
23319     {name: 'author', mapping: 'username'},
23320     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23321     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23322     {name: 'lastPoster', mapping: 'user2'},
23323     {name: 'excerpt', mapping: 'post_text'}
23324 );
23325
23326 var myNewRecord = new TopicRecord({
23327     title: 'Do my job please',
23328     author: 'noobie',
23329     totalPosts: 1,
23330     lastPost: new Date(),
23331     lastPoster: 'Animal',
23332     excerpt: 'No way dude!'
23333 });
23334 myStore.add(myNewRecord);
23335 </code></pre>
23336  * @method create
23337  * @static
23338  */
23339 Roo.data.Record.create = function(o){
23340     var f = function(){
23341         f.superclass.constructor.apply(this, arguments);
23342     };
23343     Roo.extend(f, Roo.data.Record);
23344     var p = f.prototype;
23345     p.fields = new Roo.util.MixedCollection(false, function(field){
23346         return field.name;
23347     });
23348     for(var i = 0, len = o.length; i < len; i++){
23349         p.fields.add(new Roo.data.Field(o[i]));
23350     }
23351     f.getField = function(name){
23352         return p.fields.get(name);  
23353     };
23354     return f;
23355 };
23356
23357 Roo.data.Record.AUTO_ID = 1000;
23358 Roo.data.Record.EDIT = 'edit';
23359 Roo.data.Record.REJECT = 'reject';
23360 Roo.data.Record.COMMIT = 'commit';
23361
23362 Roo.data.Record.prototype = {
23363     /**
23364      * Readonly flag - true if this record has been modified.
23365      * @type Boolean
23366      */
23367     dirty : false,
23368     editing : false,
23369     error: null,
23370     modified: null,
23371
23372     // private
23373     join : function(store){
23374         this.store = store;
23375     },
23376
23377     /**
23378      * Set the named field to the specified value.
23379      * @param {String} name The name of the field to set.
23380      * @param {Object} value The value to set the field to.
23381      */
23382     set : function(name, value){
23383         if(this.data[name] == value){
23384             return;
23385         }
23386         this.dirty = true;
23387         if(!this.modified){
23388             this.modified = {};
23389         }
23390         if(typeof this.modified[name] == 'undefined'){
23391             this.modified[name] = this.data[name];
23392         }
23393         this.data[name] = value;
23394         if(!this.editing && this.store){
23395             this.store.afterEdit(this);
23396         }       
23397     },
23398
23399     /**
23400      * Get the value of the named field.
23401      * @param {String} name The name of the field to get the value of.
23402      * @return {Object} The value of the field.
23403      */
23404     get : function(name){
23405         return this.data[name]; 
23406     },
23407
23408     // private
23409     beginEdit : function(){
23410         this.editing = true;
23411         this.modified = {}; 
23412     },
23413
23414     // private
23415     cancelEdit : function(){
23416         this.editing = false;
23417         delete this.modified;
23418     },
23419
23420     // private
23421     endEdit : function(){
23422         this.editing = false;
23423         if(this.dirty && this.store){
23424             this.store.afterEdit(this);
23425         }
23426     },
23427
23428     /**
23429      * Usually called by the {@link Roo.data.Store} which owns the Record.
23430      * Rejects all changes made to the Record since either creation, or the last commit operation.
23431      * Modified fields are reverted to their original values.
23432      * <p>
23433      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23434      * of reject operations.
23435      */
23436     reject : function(){
23437         var m = this.modified;
23438         for(var n in m){
23439             if(typeof m[n] != "function"){
23440                 this.data[n] = m[n];
23441             }
23442         }
23443         this.dirty = false;
23444         delete this.modified;
23445         this.editing = false;
23446         if(this.store){
23447             this.store.afterReject(this);
23448         }
23449     },
23450
23451     /**
23452      * Usually called by the {@link Roo.data.Store} which owns the Record.
23453      * Commits all changes made to the Record since either creation, or the last commit operation.
23454      * <p>
23455      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23456      * of commit operations.
23457      */
23458     commit : function(){
23459         this.dirty = false;
23460         delete this.modified;
23461         this.editing = false;
23462         if(this.store){
23463             this.store.afterCommit(this);
23464         }
23465     },
23466
23467     // private
23468     hasError : function(){
23469         return this.error != null;
23470     },
23471
23472     // private
23473     clearError : function(){
23474         this.error = null;
23475     },
23476
23477     /**
23478      * Creates a copy of this record.
23479      * @param {String} id (optional) A new record id if you don't want to use this record's id
23480      * @return {Record}
23481      */
23482     copy : function(newId) {
23483         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23484     }
23485 };/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496
23497
23498 /**
23499  * @class Roo.data.Store
23500  * @extends Roo.util.Observable
23501  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23502  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23503  * <p>
23504  * 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
23505  * has no knowledge of the format of the data returned by the Proxy.<br>
23506  * <p>
23507  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23508  * instances from the data object. These records are cached and made available through accessor functions.
23509  * @constructor
23510  * Creates a new Store.
23511  * @param {Object} config A config object containing the objects needed for the Store to access data,
23512  * and read the data into Records.
23513  */
23514 Roo.data.Store = function(config){
23515     this.data = new Roo.util.MixedCollection(false);
23516     this.data.getKey = function(o){
23517         return o.id;
23518     };
23519     this.baseParams = {};
23520     // private
23521     this.paramNames = {
23522         "start" : "start",
23523         "limit" : "limit",
23524         "sort" : "sort",
23525         "dir" : "dir",
23526         "multisort" : "_multisort"
23527     };
23528
23529     if(config && config.data){
23530         this.inlineData = config.data;
23531         delete config.data;
23532     }
23533
23534     Roo.apply(this, config);
23535     
23536     if(this.reader){ // reader passed
23537         this.reader = Roo.factory(this.reader, Roo.data);
23538         this.reader.xmodule = this.xmodule || false;
23539         if(!this.recordType){
23540             this.recordType = this.reader.recordType;
23541         }
23542         if(this.reader.onMetaChange){
23543             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23544         }
23545     }
23546
23547     if(this.recordType){
23548         this.fields = this.recordType.prototype.fields;
23549     }
23550     this.modified = [];
23551
23552     this.addEvents({
23553         /**
23554          * @event datachanged
23555          * Fires when the data cache has changed, and a widget which is using this Store
23556          * as a Record cache should refresh its view.
23557          * @param {Store} this
23558          */
23559         datachanged : true,
23560         /**
23561          * @event metachange
23562          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23563          * @param {Store} this
23564          * @param {Object} meta The JSON metadata
23565          */
23566         metachange : true,
23567         /**
23568          * @event add
23569          * Fires when Records have been added to the Store
23570          * @param {Store} this
23571          * @param {Roo.data.Record[]} records The array of Records added
23572          * @param {Number} index The index at which the record(s) were added
23573          */
23574         add : true,
23575         /**
23576          * @event remove
23577          * Fires when a Record has been removed from the Store
23578          * @param {Store} this
23579          * @param {Roo.data.Record} record The Record that was removed
23580          * @param {Number} index The index at which the record was removed
23581          */
23582         remove : true,
23583         /**
23584          * @event update
23585          * Fires when a Record has been updated
23586          * @param {Store} this
23587          * @param {Roo.data.Record} record The Record that was updated
23588          * @param {String} operation The update operation being performed.  Value may be one of:
23589          * <pre><code>
23590  Roo.data.Record.EDIT
23591  Roo.data.Record.REJECT
23592  Roo.data.Record.COMMIT
23593          * </code></pre>
23594          */
23595         update : true,
23596         /**
23597          * @event clear
23598          * Fires when the data cache has been cleared.
23599          * @param {Store} this
23600          */
23601         clear : true,
23602         /**
23603          * @event beforeload
23604          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23605          * the load action will be canceled.
23606          * @param {Store} this
23607          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23608          */
23609         beforeload : true,
23610         /**
23611          * @event beforeloadadd
23612          * Fires after a new set of Records has been loaded.
23613          * @param {Store} this
23614          * @param {Roo.data.Record[]} records The Records that were loaded
23615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23616          */
23617         beforeloadadd : true,
23618         /**
23619          * @event load
23620          * Fires after a new set of Records has been loaded, before they are added to the store.
23621          * @param {Store} this
23622          * @param {Roo.data.Record[]} records The Records that were loaded
23623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23624          * @params {Object} return from reader
23625          */
23626         load : true,
23627         /**
23628          * @event loadexception
23629          * Fires if an exception occurs in the Proxy during loading.
23630          * Called with the signature of the Proxy's "loadexception" event.
23631          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23632          * 
23633          * @param {Proxy} 
23634          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23635          * @param {Object} load options 
23636          * @param {Object} jsonData from your request (normally this contains the Exception)
23637          */
23638         loadexception : true
23639     });
23640     
23641     if(this.proxy){
23642         this.proxy = Roo.factory(this.proxy, Roo.data);
23643         this.proxy.xmodule = this.xmodule || false;
23644         this.relayEvents(this.proxy,  ["loadexception"]);
23645     }
23646     this.sortToggle = {};
23647     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23648
23649     Roo.data.Store.superclass.constructor.call(this);
23650
23651     if(this.inlineData){
23652         this.loadData(this.inlineData);
23653         delete this.inlineData;
23654     }
23655 };
23656
23657 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23658      /**
23659     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23660     * without a remote query - used by combo/forms at present.
23661     */
23662     
23663     /**
23664     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23665     */
23666     /**
23667     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23668     */
23669     /**
23670     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23671     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23672     */
23673     /**
23674     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23675     * on any HTTP request
23676     */
23677     /**
23678     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23679     */
23680     /**
23681     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23682     */
23683     multiSort: false,
23684     /**
23685     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23686     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23687     */
23688     remoteSort : false,
23689
23690     /**
23691     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23692      * loaded or when a record is removed. (defaults to false).
23693     */
23694     pruneModifiedRecords : false,
23695
23696     // private
23697     lastOptions : null,
23698
23699     /**
23700      * Add Records to the Store and fires the add event.
23701      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23702      */
23703     add : function(records){
23704         records = [].concat(records);
23705         for(var i = 0, len = records.length; i < len; i++){
23706             records[i].join(this);
23707         }
23708         var index = this.data.length;
23709         this.data.addAll(records);
23710         this.fireEvent("add", this, records, index);
23711     },
23712
23713     /**
23714      * Remove a Record from the Store and fires the remove event.
23715      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23716      */
23717     remove : function(record){
23718         var index = this.data.indexOf(record);
23719         this.data.removeAt(index);
23720  
23721         if(this.pruneModifiedRecords){
23722             this.modified.remove(record);
23723         }
23724         this.fireEvent("remove", this, record, index);
23725     },
23726
23727     /**
23728      * Remove all Records from the Store and fires the clear event.
23729      */
23730     removeAll : function(){
23731         this.data.clear();
23732         if(this.pruneModifiedRecords){
23733             this.modified = [];
23734         }
23735         this.fireEvent("clear", this);
23736     },
23737
23738     /**
23739      * Inserts Records to the Store at the given index and fires the add event.
23740      * @param {Number} index The start index at which to insert the passed Records.
23741      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23742      */
23743     insert : function(index, records){
23744         records = [].concat(records);
23745         for(var i = 0, len = records.length; i < len; i++){
23746             this.data.insert(index, records[i]);
23747             records[i].join(this);
23748         }
23749         this.fireEvent("add", this, records, index);
23750     },
23751
23752     /**
23753      * Get the index within the cache of the passed Record.
23754      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23755      * @return {Number} The index of the passed Record. Returns -1 if not found.
23756      */
23757     indexOf : function(record){
23758         return this.data.indexOf(record);
23759     },
23760
23761     /**
23762      * Get the index within the cache of the Record with the passed id.
23763      * @param {String} id The id of the Record to find.
23764      * @return {Number} The index of the Record. Returns -1 if not found.
23765      */
23766     indexOfId : function(id){
23767         return this.data.indexOfKey(id);
23768     },
23769
23770     /**
23771      * Get the Record with the specified id.
23772      * @param {String} id The id of the Record to find.
23773      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23774      */
23775     getById : function(id){
23776         return this.data.key(id);
23777     },
23778
23779     /**
23780      * Get the Record at the specified index.
23781      * @param {Number} index The index of the Record to find.
23782      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23783      */
23784     getAt : function(index){
23785         return this.data.itemAt(index);
23786     },
23787
23788     /**
23789      * Returns a range of Records between specified indices.
23790      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23791      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23792      * @return {Roo.data.Record[]} An array of Records
23793      */
23794     getRange : function(start, end){
23795         return this.data.getRange(start, end);
23796     },
23797
23798     // private
23799     storeOptions : function(o){
23800         o = Roo.apply({}, o);
23801         delete o.callback;
23802         delete o.scope;
23803         this.lastOptions = o;
23804     },
23805
23806     /**
23807      * Loads the Record cache from the configured Proxy using the configured Reader.
23808      * <p>
23809      * If using remote paging, then the first load call must specify the <em>start</em>
23810      * and <em>limit</em> properties in the options.params property to establish the initial
23811      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23812      * <p>
23813      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23814      * and this call will return before the new data has been loaded. Perform any post-processing
23815      * in a callback function, or in a "load" event handler.</strong>
23816      * <p>
23817      * @param {Object} options An object containing properties which control loading options:<ul>
23818      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23819      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23820      * passed the following arguments:<ul>
23821      * <li>r : Roo.data.Record[]</li>
23822      * <li>options: Options object from the load call</li>
23823      * <li>success: Boolean success indicator</li></ul></li>
23824      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23825      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23826      * </ul>
23827      */
23828     load : function(options){
23829         options = options || {};
23830         if(this.fireEvent("beforeload", this, options) !== false){
23831             this.storeOptions(options);
23832             var p = Roo.apply(options.params || {}, this.baseParams);
23833             // if meta was not loaded from remote source.. try requesting it.
23834             if (!this.reader.metaFromRemote) {
23835                 p._requestMeta = 1;
23836             }
23837             if(this.sortInfo && this.remoteSort){
23838                 var pn = this.paramNames;
23839                 p[pn["sort"]] = this.sortInfo.field;
23840                 p[pn["dir"]] = this.sortInfo.direction;
23841             }
23842             if (this.multiSort) {
23843                 var pn = this.paramNames;
23844                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23845             }
23846             
23847             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23848         }
23849     },
23850
23851     /**
23852      * Reloads the Record cache from the configured Proxy using the configured Reader and
23853      * the options from the last load operation performed.
23854      * @param {Object} options (optional) An object containing properties which may override the options
23855      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23856      * the most recently used options are reused).
23857      */
23858     reload : function(options){
23859         this.load(Roo.applyIf(options||{}, this.lastOptions));
23860     },
23861
23862     // private
23863     // Called as a callback by the Reader during a load operation.
23864     loadRecords : function(o, options, success){
23865         if(!o || success === false){
23866             if(success !== false){
23867                 this.fireEvent("load", this, [], options, o);
23868             }
23869             if(options.callback){
23870                 options.callback.call(options.scope || this, [], options, false);
23871             }
23872             return;
23873         }
23874         // if data returned failure - throw an exception.
23875         if (o.success === false) {
23876             // show a message if no listener is registered.
23877             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23878                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23879             }
23880             // loadmask wil be hooked into this..
23881             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23882             return;
23883         }
23884         var r = o.records, t = o.totalRecords || r.length;
23885         
23886         this.fireEvent("beforeloadadd", this, r, options, o);
23887         
23888         if(!options || options.add !== true){
23889             if(this.pruneModifiedRecords){
23890                 this.modified = [];
23891             }
23892             for(var i = 0, len = r.length; i < len; i++){
23893                 r[i].join(this);
23894             }
23895             if(this.snapshot){
23896                 this.data = this.snapshot;
23897                 delete this.snapshot;
23898             }
23899             this.data.clear();
23900             this.data.addAll(r);
23901             this.totalLength = t;
23902             this.applySort();
23903             this.fireEvent("datachanged", this);
23904         }else{
23905             this.totalLength = Math.max(t, this.data.length+r.length);
23906             this.add(r);
23907         }
23908         
23909         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23910                 
23911             var e = new Roo.data.Record({});
23912
23913             e.set(this.parent.displayField, this.parent.emptyTitle);
23914             e.set(this.parent.valueField, '');
23915
23916             this.insert(0, e);
23917         }
23918             
23919         this.fireEvent("load", this, r, options, o);
23920         if(options.callback){
23921             options.callback.call(options.scope || this, r, options, true);
23922         }
23923     },
23924
23925
23926     /**
23927      * Loads data from a passed data block. A Reader which understands the format of the data
23928      * must have been configured in the constructor.
23929      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23930      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23931      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23932      */
23933     loadData : function(o, append){
23934         var r = this.reader.readRecords(o);
23935         this.loadRecords(r, {add: append}, true);
23936     },
23937     
23938      /**
23939      * using 'cn' the nested child reader read the child array into it's child stores.
23940      * @param {Object} rec The record with a 'children array
23941      */
23942     loadDataFromChildren : function(rec)
23943     {
23944         this.loadData(this.reader.toLoadData(rec));
23945     },
23946     
23947
23948     /**
23949      * Gets the number of cached records.
23950      * <p>
23951      * <em>If using paging, this may not be the total size of the dataset. If the data object
23952      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23953      * the data set size</em>
23954      */
23955     getCount : function(){
23956         return this.data.length || 0;
23957     },
23958
23959     /**
23960      * Gets the total number of records in the dataset as returned by the server.
23961      * <p>
23962      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23963      * the dataset size</em>
23964      */
23965     getTotalCount : function(){
23966         return this.totalLength || 0;
23967     },
23968
23969     /**
23970      * Returns the sort state of the Store as an object with two properties:
23971      * <pre><code>
23972  field {String} The name of the field by which the Records are sorted
23973  direction {String} The sort order, "ASC" or "DESC"
23974      * </code></pre>
23975      */
23976     getSortState : function(){
23977         return this.sortInfo;
23978     },
23979
23980     // private
23981     applySort : function(){
23982         if(this.sortInfo && !this.remoteSort){
23983             var s = this.sortInfo, f = s.field;
23984             var st = this.fields.get(f).sortType;
23985             var fn = function(r1, r2){
23986                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23987                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23988             };
23989             this.data.sort(s.direction, fn);
23990             if(this.snapshot && this.snapshot != this.data){
23991                 this.snapshot.sort(s.direction, fn);
23992             }
23993         }
23994     },
23995
23996     /**
23997      * Sets the default sort column and order to be used by the next load operation.
23998      * @param {String} fieldName The name of the field to sort by.
23999      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24000      */
24001     setDefaultSort : function(field, dir){
24002         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24003     },
24004
24005     /**
24006      * Sort the Records.
24007      * If remote sorting is used, the sort is performed on the server, and the cache is
24008      * reloaded. If local sorting is used, the cache is sorted internally.
24009      * @param {String} fieldName The name of the field to sort by.
24010      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24011      */
24012     sort : function(fieldName, dir){
24013         var f = this.fields.get(fieldName);
24014         if(!dir){
24015             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24016             
24017             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24018                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24019             }else{
24020                 dir = f.sortDir;
24021             }
24022         }
24023         this.sortToggle[f.name] = dir;
24024         this.sortInfo = {field: f.name, direction: dir};
24025         if(!this.remoteSort){
24026             this.applySort();
24027             this.fireEvent("datachanged", this);
24028         }else{
24029             this.load(this.lastOptions);
24030         }
24031     },
24032
24033     /**
24034      * Calls the specified function for each of the Records in the cache.
24035      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24036      * Returning <em>false</em> aborts and exits the iteration.
24037      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24038      */
24039     each : function(fn, scope){
24040         this.data.each(fn, scope);
24041     },
24042
24043     /**
24044      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24045      * (e.g., during paging).
24046      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24047      */
24048     getModifiedRecords : function(){
24049         return this.modified;
24050     },
24051
24052     // private
24053     createFilterFn : function(property, value, anyMatch){
24054         if(!value.exec){ // not a regex
24055             value = String(value);
24056             if(value.length == 0){
24057                 return false;
24058             }
24059             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24060         }
24061         return function(r){
24062             return value.test(r.data[property]);
24063         };
24064     },
24065
24066     /**
24067      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24068      * @param {String} property A field on your records
24069      * @param {Number} start The record index to start at (defaults to 0)
24070      * @param {Number} end The last record index to include (defaults to length - 1)
24071      * @return {Number} The sum
24072      */
24073     sum : function(property, start, end){
24074         var rs = this.data.items, v = 0;
24075         start = start || 0;
24076         end = (end || end === 0) ? end : rs.length-1;
24077
24078         for(var i = start; i <= end; i++){
24079             v += (rs[i].data[property] || 0);
24080         }
24081         return v;
24082     },
24083
24084     /**
24085      * Filter the records by a specified property.
24086      * @param {String} field A field on your records
24087      * @param {String/RegExp} value Either a string that the field
24088      * should start with or a RegExp to test against the field
24089      * @param {Boolean} anyMatch True to match any part not just the beginning
24090      */
24091     filter : function(property, value, anyMatch){
24092         var fn = this.createFilterFn(property, value, anyMatch);
24093         return fn ? this.filterBy(fn) : this.clearFilter();
24094     },
24095
24096     /**
24097      * Filter by a function. The specified function will be called with each
24098      * record in this data source. If the function returns true the record is included,
24099      * otherwise it is filtered.
24100      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24101      * @param {Object} scope (optional) The scope of the function (defaults to this)
24102      */
24103     filterBy : function(fn, scope){
24104         this.snapshot = this.snapshot || this.data;
24105         this.data = this.queryBy(fn, scope||this);
24106         this.fireEvent("datachanged", this);
24107     },
24108
24109     /**
24110      * Query the records by a specified property.
24111      * @param {String} field A field on your records
24112      * @param {String/RegExp} value Either a string that the field
24113      * should start with or a RegExp to test against the field
24114      * @param {Boolean} anyMatch True to match any part not just the beginning
24115      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24116      */
24117     query : function(property, value, anyMatch){
24118         var fn = this.createFilterFn(property, value, anyMatch);
24119         return fn ? this.queryBy(fn) : this.data.clone();
24120     },
24121
24122     /**
24123      * Query by a function. The specified function will be called with each
24124      * record in this data source. If the function returns true the record is included
24125      * in the results.
24126      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24127      * @param {Object} scope (optional) The scope of the function (defaults to this)
24128       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24129      **/
24130     queryBy : function(fn, scope){
24131         var data = this.snapshot || this.data;
24132         return data.filterBy(fn, scope||this);
24133     },
24134
24135     /**
24136      * Collects unique values for a particular dataIndex from this store.
24137      * @param {String} dataIndex The property to collect
24138      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24139      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24140      * @return {Array} An array of the unique values
24141      **/
24142     collect : function(dataIndex, allowNull, bypassFilter){
24143         var d = (bypassFilter === true && this.snapshot) ?
24144                 this.snapshot.items : this.data.items;
24145         var v, sv, r = [], l = {};
24146         for(var i = 0, len = d.length; i < len; i++){
24147             v = d[i].data[dataIndex];
24148             sv = String(v);
24149             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24150                 l[sv] = true;
24151                 r[r.length] = v;
24152             }
24153         }
24154         return r;
24155     },
24156
24157     /**
24158      * Revert to a view of the Record cache with no filtering applied.
24159      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24160      */
24161     clearFilter : function(suppressEvent){
24162         if(this.snapshot && this.snapshot != this.data){
24163             this.data = this.snapshot;
24164             delete this.snapshot;
24165             if(suppressEvent !== true){
24166                 this.fireEvent("datachanged", this);
24167             }
24168         }
24169     },
24170
24171     // private
24172     afterEdit : function(record){
24173         if(this.modified.indexOf(record) == -1){
24174             this.modified.push(record);
24175         }
24176         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24177     },
24178     
24179     // private
24180     afterReject : function(record){
24181         this.modified.remove(record);
24182         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24183     },
24184
24185     // private
24186     afterCommit : function(record){
24187         this.modified.remove(record);
24188         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24189     },
24190
24191     /**
24192      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24193      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24194      */
24195     commitChanges : function(){
24196         var m = this.modified.slice(0);
24197         this.modified = [];
24198         for(var i = 0, len = m.length; i < len; i++){
24199             m[i].commit();
24200         }
24201     },
24202
24203     /**
24204      * Cancel outstanding changes on all changed records.
24205      */
24206     rejectChanges : function(){
24207         var m = this.modified.slice(0);
24208         this.modified = [];
24209         for(var i = 0, len = m.length; i < len; i++){
24210             m[i].reject();
24211         }
24212     },
24213
24214     onMetaChange : function(meta, rtype, o){
24215         this.recordType = rtype;
24216         this.fields = rtype.prototype.fields;
24217         delete this.snapshot;
24218         this.sortInfo = meta.sortInfo || this.sortInfo;
24219         this.modified = [];
24220         this.fireEvent('metachange', this, this.reader.meta);
24221     },
24222     
24223     moveIndex : function(data, type)
24224     {
24225         var index = this.indexOf(data);
24226         
24227         var newIndex = index + type;
24228         
24229         this.remove(data);
24230         
24231         this.insert(newIndex, data);
24232         
24233     }
24234 });/*
24235  * Based on:
24236  * Ext JS Library 1.1.1
24237  * Copyright(c) 2006-2007, Ext JS, LLC.
24238  *
24239  * Originally Released Under LGPL - original licence link has changed is not relivant.
24240  *
24241  * Fork - LGPL
24242  * <script type="text/javascript">
24243  */
24244
24245 /**
24246  * @class Roo.data.SimpleStore
24247  * @extends Roo.data.Store
24248  * Small helper class to make creating Stores from Array data easier.
24249  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24250  * @cfg {Array} fields An array of field definition objects, or field name strings.
24251  * @cfg {Object} an existing reader (eg. copied from another store)
24252  * @cfg {Array} data The multi-dimensional array of data
24253  * @constructor
24254  * @param {Object} config
24255  */
24256 Roo.data.SimpleStore = function(config)
24257 {
24258     Roo.data.SimpleStore.superclass.constructor.call(this, {
24259         isLocal : true,
24260         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24261                 id: config.id
24262             },
24263             Roo.data.Record.create(config.fields)
24264         ),
24265         proxy : new Roo.data.MemoryProxy(config.data)
24266     });
24267     this.load();
24268 };
24269 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24270  * Based on:
24271  * Ext JS Library 1.1.1
24272  * Copyright(c) 2006-2007, Ext JS, LLC.
24273  *
24274  * Originally Released Under LGPL - original licence link has changed is not relivant.
24275  *
24276  * Fork - LGPL
24277  * <script type="text/javascript">
24278  */
24279
24280 /**
24281 /**
24282  * @extends Roo.data.Store
24283  * @class Roo.data.JsonStore
24284  * Small helper class to make creating Stores for JSON data easier. <br/>
24285 <pre><code>
24286 var store = new Roo.data.JsonStore({
24287     url: 'get-images.php',
24288     root: 'images',
24289     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24290 });
24291 </code></pre>
24292  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24293  * JsonReader and HttpProxy (unless inline data is provided).</b>
24294  * @cfg {Array} fields An array of field definition objects, or field name strings.
24295  * @constructor
24296  * @param {Object} config
24297  */
24298 Roo.data.JsonStore = function(c){
24299     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24300         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24301         reader: new Roo.data.JsonReader(c, c.fields)
24302     }));
24303 };
24304 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
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 Roo.data.Field = function(config){
24317     if(typeof config == "string"){
24318         config = {name: config};
24319     }
24320     Roo.apply(this, config);
24321     
24322     if(!this.type){
24323         this.type = "auto";
24324     }
24325     
24326     var st = Roo.data.SortTypes;
24327     // named sortTypes are supported, here we look them up
24328     if(typeof this.sortType == "string"){
24329         this.sortType = st[this.sortType];
24330     }
24331     
24332     // set default sortType for strings and dates
24333     if(!this.sortType){
24334         switch(this.type){
24335             case "string":
24336                 this.sortType = st.asUCString;
24337                 break;
24338             case "date":
24339                 this.sortType = st.asDate;
24340                 break;
24341             default:
24342                 this.sortType = st.none;
24343         }
24344     }
24345
24346     // define once
24347     var stripRe = /[\$,%]/g;
24348
24349     // prebuilt conversion function for this field, instead of
24350     // switching every time we're reading a value
24351     if(!this.convert){
24352         var cv, dateFormat = this.dateFormat;
24353         switch(this.type){
24354             case "":
24355             case "auto":
24356             case undefined:
24357                 cv = function(v){ return v; };
24358                 break;
24359             case "string":
24360                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24361                 break;
24362             case "int":
24363                 cv = function(v){
24364                     return v !== undefined && v !== null && v !== '' ?
24365                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24366                     };
24367                 break;
24368             case "float":
24369                 cv = function(v){
24370                     return v !== undefined && v !== null && v !== '' ?
24371                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24372                     };
24373                 break;
24374             case "bool":
24375             case "boolean":
24376                 cv = function(v){ return v === true || v === "true" || v == 1; };
24377                 break;
24378             case "date":
24379                 cv = function(v){
24380                     if(!v){
24381                         return '';
24382                     }
24383                     if(v instanceof Date){
24384                         return v;
24385                     }
24386                     if(dateFormat){
24387                         if(dateFormat == "timestamp"){
24388                             return new Date(v*1000);
24389                         }
24390                         return Date.parseDate(v, dateFormat);
24391                     }
24392                     var parsed = Date.parse(v);
24393                     return parsed ? new Date(parsed) : null;
24394                 };
24395              break;
24396             
24397         }
24398         this.convert = cv;
24399     }
24400 };
24401
24402 Roo.data.Field.prototype = {
24403     dateFormat: null,
24404     defaultValue: "",
24405     mapping: null,
24406     sortType : null,
24407     sortDir : "ASC"
24408 };/*
24409  * Based on:
24410  * Ext JS Library 1.1.1
24411  * Copyright(c) 2006-2007, Ext JS, LLC.
24412  *
24413  * Originally Released Under LGPL - original licence link has changed is not relivant.
24414  *
24415  * Fork - LGPL
24416  * <script type="text/javascript">
24417  */
24418  
24419 // Base class for reading structured data from a data source.  This class is intended to be
24420 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24421
24422 /**
24423  * @class Roo.data.DataReader
24424  * Base class for reading structured data from a data source.  This class is intended to be
24425  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24426  */
24427
24428 Roo.data.DataReader = function(meta, recordType){
24429     
24430     this.meta = meta;
24431     
24432     this.recordType = recordType instanceof Array ? 
24433         Roo.data.Record.create(recordType) : recordType;
24434 };
24435
24436 Roo.data.DataReader.prototype = {
24437     
24438     
24439     readerType : 'Data',
24440      /**
24441      * Create an empty record
24442      * @param {Object} data (optional) - overlay some values
24443      * @return {Roo.data.Record} record created.
24444      */
24445     newRow :  function(d) {
24446         var da =  {};
24447         this.recordType.prototype.fields.each(function(c) {
24448             switch( c.type) {
24449                 case 'int' : da[c.name] = 0; break;
24450                 case 'date' : da[c.name] = new Date(); break;
24451                 case 'float' : da[c.name] = 0.0; break;
24452                 case 'boolean' : da[c.name] = false; break;
24453                 default : da[c.name] = ""; break;
24454             }
24455             
24456         });
24457         return new this.recordType(Roo.apply(da, d));
24458     }
24459     
24460     
24461 };/*
24462  * Based on:
24463  * Ext JS Library 1.1.1
24464  * Copyright(c) 2006-2007, Ext JS, LLC.
24465  *
24466  * Originally Released Under LGPL - original licence link has changed is not relivant.
24467  *
24468  * Fork - LGPL
24469  * <script type="text/javascript">
24470  */
24471
24472 /**
24473  * @class Roo.data.DataProxy
24474  * @extends Roo.data.Observable
24475  * This class is an abstract base class for implementations which provide retrieval of
24476  * unformatted data objects.<br>
24477  * <p>
24478  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24479  * (of the appropriate type which knows how to parse the data object) to provide a block of
24480  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24481  * <p>
24482  * Custom implementations must implement the load method as described in
24483  * {@link Roo.data.HttpProxy#load}.
24484  */
24485 Roo.data.DataProxy = function(){
24486     this.addEvents({
24487         /**
24488          * @event beforeload
24489          * Fires before a network request is made to retrieve a data object.
24490          * @param {Object} This DataProxy object.
24491          * @param {Object} params The params parameter to the load function.
24492          */
24493         beforeload : true,
24494         /**
24495          * @event load
24496          * Fires before the load method's callback is called.
24497          * @param {Object} This DataProxy object.
24498          * @param {Object} o The data object.
24499          * @param {Object} arg The callback argument object passed to the load function.
24500          */
24501         load : true,
24502         /**
24503          * @event loadexception
24504          * Fires if an Exception occurs during data retrieval.
24505          * @param {Object} This DataProxy object.
24506          * @param {Object} o The data object.
24507          * @param {Object} arg The callback argument object passed to the load function.
24508          * @param {Object} e The Exception.
24509          */
24510         loadexception : true
24511     });
24512     Roo.data.DataProxy.superclass.constructor.call(this);
24513 };
24514
24515 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24516
24517     /**
24518      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24519      */
24520 /*
24521  * Based on:
24522  * Ext JS Library 1.1.1
24523  * Copyright(c) 2006-2007, Ext JS, LLC.
24524  *
24525  * Originally Released Under LGPL - original licence link has changed is not relivant.
24526  *
24527  * Fork - LGPL
24528  * <script type="text/javascript">
24529  */
24530 /**
24531  * @class Roo.data.MemoryProxy
24532  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24533  * to the Reader when its load method is called.
24534  * @constructor
24535  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24536  */
24537 Roo.data.MemoryProxy = function(data){
24538     if (data.data) {
24539         data = data.data;
24540     }
24541     Roo.data.MemoryProxy.superclass.constructor.call(this);
24542     this.data = data;
24543 };
24544
24545 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24546     
24547     /**
24548      * Load data from the requested source (in this case an in-memory
24549      * data object passed to the constructor), read the data object into
24550      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24551      * process that block using the passed callback.
24552      * @param {Object} params This parameter is not used by the MemoryProxy class.
24553      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24554      * object into a block of Roo.data.Records.
24555      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24556      * The function must be passed <ul>
24557      * <li>The Record block object</li>
24558      * <li>The "arg" argument from the load function</li>
24559      * <li>A boolean success indicator</li>
24560      * </ul>
24561      * @param {Object} scope The scope in which to call the callback
24562      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24563      */
24564     load : function(params, reader, callback, scope, arg){
24565         params = params || {};
24566         var result;
24567         try {
24568             result = reader.readRecords(params.data ? params.data :this.data);
24569         }catch(e){
24570             this.fireEvent("loadexception", this, arg, null, e);
24571             callback.call(scope, null, arg, false);
24572             return;
24573         }
24574         callback.call(scope, result, arg, true);
24575     },
24576     
24577     // private
24578     update : function(params, records){
24579         
24580     }
24581 });/*
24582  * Based on:
24583  * Ext JS Library 1.1.1
24584  * Copyright(c) 2006-2007, Ext JS, LLC.
24585  *
24586  * Originally Released Under LGPL - original licence link has changed is not relivant.
24587  *
24588  * Fork - LGPL
24589  * <script type="text/javascript">
24590  */
24591 /**
24592  * @class Roo.data.HttpProxy
24593  * @extends Roo.data.DataProxy
24594  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24595  * configured to reference a certain URL.<br><br>
24596  * <p>
24597  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24598  * from which the running page was served.<br><br>
24599  * <p>
24600  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24601  * <p>
24602  * Be aware that to enable the browser to parse an XML document, the server must set
24603  * the Content-Type header in the HTTP response to "text/xml".
24604  * @constructor
24605  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24606  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24607  * will be used to make the request.
24608  */
24609 Roo.data.HttpProxy = function(conn){
24610     Roo.data.HttpProxy.superclass.constructor.call(this);
24611     // is conn a conn config or a real conn?
24612     this.conn = conn;
24613     this.useAjax = !conn || !conn.events;
24614   
24615 };
24616
24617 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24618     // thse are take from connection...
24619     
24620     /**
24621      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24622      */
24623     /**
24624      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24625      * extra parameters to each request made by this object. (defaults to undefined)
24626      */
24627     /**
24628      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24629      *  to each request made by this object. (defaults to undefined)
24630      */
24631     /**
24632      * @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)
24633      */
24634     /**
24635      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24636      */
24637      /**
24638      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24639      * @type Boolean
24640      */
24641   
24642
24643     /**
24644      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24645      * @type Boolean
24646      */
24647     /**
24648      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24649      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24650      * a finer-grained basis than the DataProxy events.
24651      */
24652     getConnection : function(){
24653         return this.useAjax ? Roo.Ajax : this.conn;
24654     },
24655
24656     /**
24657      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24658      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24659      * process that block using the passed callback.
24660      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24661      * for the request to the remote server.
24662      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24663      * object into a block of Roo.data.Records.
24664      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24665      * The function must be passed <ul>
24666      * <li>The Record block object</li>
24667      * <li>The "arg" argument from the load function</li>
24668      * <li>A boolean success indicator</li>
24669      * </ul>
24670      * @param {Object} scope The scope in which to call the callback
24671      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24672      */
24673     load : function(params, reader, callback, scope, arg){
24674         if(this.fireEvent("beforeload", this, params) !== false){
24675             var  o = {
24676                 params : params || {},
24677                 request: {
24678                     callback : callback,
24679                     scope : scope,
24680                     arg : arg
24681                 },
24682                 reader: reader,
24683                 callback : this.loadResponse,
24684                 scope: this
24685             };
24686             if(this.useAjax){
24687                 Roo.applyIf(o, this.conn);
24688                 if(this.activeRequest){
24689                     Roo.Ajax.abort(this.activeRequest);
24690                 }
24691                 this.activeRequest = Roo.Ajax.request(o);
24692             }else{
24693                 this.conn.request(o);
24694             }
24695         }else{
24696             callback.call(scope||this, null, arg, false);
24697         }
24698     },
24699
24700     // private
24701     loadResponse : function(o, success, response){
24702         delete this.activeRequest;
24703         if(!success){
24704             this.fireEvent("loadexception", this, o, response);
24705             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24706             return;
24707         }
24708         var result;
24709         try {
24710             result = o.reader.read(response);
24711         }catch(e){
24712             this.fireEvent("loadexception", this, o, response, e);
24713             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24714             return;
24715         }
24716         
24717         this.fireEvent("load", this, o, o.request.arg);
24718         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24719     },
24720
24721     // private
24722     update : function(dataSet){
24723
24724     },
24725
24726     // private
24727     updateResponse : function(dataSet){
24728
24729     }
24730 });/*
24731  * Based on:
24732  * Ext JS Library 1.1.1
24733  * Copyright(c) 2006-2007, Ext JS, LLC.
24734  *
24735  * Originally Released Under LGPL - original licence link has changed is not relivant.
24736  *
24737  * Fork - LGPL
24738  * <script type="text/javascript">
24739  */
24740
24741 /**
24742  * @class Roo.data.ScriptTagProxy
24743  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24744  * other than the originating domain of the running page.<br><br>
24745  * <p>
24746  * <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
24747  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24748  * <p>
24749  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24750  * source code that is used as the source inside a &lt;script> tag.<br><br>
24751  * <p>
24752  * In order for the browser to process the returned data, the server must wrap the data object
24753  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24754  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24755  * depending on whether the callback name was passed:
24756  * <p>
24757  * <pre><code>
24758 boolean scriptTag = false;
24759 String cb = request.getParameter("callback");
24760 if (cb != null) {
24761     scriptTag = true;
24762     response.setContentType("text/javascript");
24763 } else {
24764     response.setContentType("application/x-json");
24765 }
24766 Writer out = response.getWriter();
24767 if (scriptTag) {
24768     out.write(cb + "(");
24769 }
24770 out.print(dataBlock.toJsonString());
24771 if (scriptTag) {
24772     out.write(");");
24773 }
24774 </pre></code>
24775  *
24776  * @constructor
24777  * @param {Object} config A configuration object.
24778  */
24779 Roo.data.ScriptTagProxy = function(config){
24780     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24781     Roo.apply(this, config);
24782     this.head = document.getElementsByTagName("head")[0];
24783 };
24784
24785 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24786
24787 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24788     /**
24789      * @cfg {String} url The URL from which to request the data object.
24790      */
24791     /**
24792      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24793      */
24794     timeout : 30000,
24795     /**
24796      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24797      * the server the name of the callback function set up by the load call to process the returned data object.
24798      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24799      * javascript output which calls this named function passing the data object as its only parameter.
24800      */
24801     callbackParam : "callback",
24802     /**
24803      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24804      * name to the request.
24805      */
24806     nocache : true,
24807
24808     /**
24809      * Load data from the configured URL, read the data object into
24810      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24811      * process that block using the passed callback.
24812      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24813      * for the request to the remote server.
24814      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24815      * object into a block of Roo.data.Records.
24816      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24817      * The function must be passed <ul>
24818      * <li>The Record block object</li>
24819      * <li>The "arg" argument from the load function</li>
24820      * <li>A boolean success indicator</li>
24821      * </ul>
24822      * @param {Object} scope The scope in which to call the callback
24823      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24824      */
24825     load : function(params, reader, callback, scope, arg){
24826         if(this.fireEvent("beforeload", this, params) !== false){
24827
24828             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24829
24830             var url = this.url;
24831             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24832             if(this.nocache){
24833                 url += "&_dc=" + (new Date().getTime());
24834             }
24835             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24836             var trans = {
24837                 id : transId,
24838                 cb : "stcCallback"+transId,
24839                 scriptId : "stcScript"+transId,
24840                 params : params,
24841                 arg : arg,
24842                 url : url,
24843                 callback : callback,
24844                 scope : scope,
24845                 reader : reader
24846             };
24847             var conn = this;
24848
24849             window[trans.cb] = function(o){
24850                 conn.handleResponse(o, trans);
24851             };
24852
24853             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24854
24855             if(this.autoAbort !== false){
24856                 this.abort();
24857             }
24858
24859             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24860
24861             var script = document.createElement("script");
24862             script.setAttribute("src", url);
24863             script.setAttribute("type", "text/javascript");
24864             script.setAttribute("id", trans.scriptId);
24865             this.head.appendChild(script);
24866
24867             this.trans = trans;
24868         }else{
24869             callback.call(scope||this, null, arg, false);
24870         }
24871     },
24872
24873     // private
24874     isLoading : function(){
24875         return this.trans ? true : false;
24876     },
24877
24878     /**
24879      * Abort the current server request.
24880      */
24881     abort : function(){
24882         if(this.isLoading()){
24883             this.destroyTrans(this.trans);
24884         }
24885     },
24886
24887     // private
24888     destroyTrans : function(trans, isLoaded){
24889         this.head.removeChild(document.getElementById(trans.scriptId));
24890         clearTimeout(trans.timeoutId);
24891         if(isLoaded){
24892             window[trans.cb] = undefined;
24893             try{
24894                 delete window[trans.cb];
24895             }catch(e){}
24896         }else{
24897             // if hasn't been loaded, wait for load to remove it to prevent script error
24898             window[trans.cb] = function(){
24899                 window[trans.cb] = undefined;
24900                 try{
24901                     delete window[trans.cb];
24902                 }catch(e){}
24903             };
24904         }
24905     },
24906
24907     // private
24908     handleResponse : function(o, trans){
24909         this.trans = false;
24910         this.destroyTrans(trans, true);
24911         var result;
24912         try {
24913             result = trans.reader.readRecords(o);
24914         }catch(e){
24915             this.fireEvent("loadexception", this, o, trans.arg, e);
24916             trans.callback.call(trans.scope||window, null, trans.arg, false);
24917             return;
24918         }
24919         this.fireEvent("load", this, o, trans.arg);
24920         trans.callback.call(trans.scope||window, result, trans.arg, true);
24921     },
24922
24923     // private
24924     handleFailure : function(trans){
24925         this.trans = false;
24926         this.destroyTrans(trans, false);
24927         this.fireEvent("loadexception", this, null, trans.arg);
24928         trans.callback.call(trans.scope||window, null, trans.arg, false);
24929     }
24930 });/*
24931  * Based on:
24932  * Ext JS Library 1.1.1
24933  * Copyright(c) 2006-2007, Ext JS, LLC.
24934  *
24935  * Originally Released Under LGPL - original licence link has changed is not relivant.
24936  *
24937  * Fork - LGPL
24938  * <script type="text/javascript">
24939  */
24940
24941 /**
24942  * @class Roo.data.JsonReader
24943  * @extends Roo.data.DataReader
24944  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24945  * based on mappings in a provided Roo.data.Record constructor.
24946  * 
24947  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24948  * in the reply previously. 
24949  * 
24950  * <p>
24951  * Example code:
24952  * <pre><code>
24953 var RecordDef = Roo.data.Record.create([
24954     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24955     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24956 ]);
24957 var myReader = new Roo.data.JsonReader({
24958     totalProperty: "results",    // The property which contains the total dataset size (optional)
24959     root: "rows",                // The property which contains an Array of row objects
24960     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24961 }, RecordDef);
24962 </code></pre>
24963  * <p>
24964  * This would consume a JSON file like this:
24965  * <pre><code>
24966 { 'results': 2, 'rows': [
24967     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24968     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24969 }
24970 </code></pre>
24971  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24972  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24973  * paged from the remote server.
24974  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24975  * @cfg {String} root name of the property which contains the Array of row objects.
24976  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24977  * @cfg {Array} fields Array of field definition objects
24978  * @constructor
24979  * Create a new JsonReader
24980  * @param {Object} meta Metadata configuration options
24981  * @param {Object} recordType Either an Array of field definition objects,
24982  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24983  */
24984 Roo.data.JsonReader = function(meta, recordType){
24985     
24986     meta = meta || {};
24987     // set some defaults:
24988     Roo.applyIf(meta, {
24989         totalProperty: 'total',
24990         successProperty : 'success',
24991         root : 'data',
24992         id : 'id'
24993     });
24994     
24995     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24996 };
24997 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24998     
24999     readerType : 'Json',
25000     
25001     /**
25002      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25003      * Used by Store query builder to append _requestMeta to params.
25004      * 
25005      */
25006     metaFromRemote : false,
25007     /**
25008      * This method is only used by a DataProxy which has retrieved data from a remote server.
25009      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25010      * @return {Object} data A data block which is used by an Roo.data.Store object as
25011      * a cache of Roo.data.Records.
25012      */
25013     read : function(response){
25014         var json = response.responseText;
25015        
25016         var o = /* eval:var:o */ eval("("+json+")");
25017         if(!o) {
25018             throw {message: "JsonReader.read: Json object not found"};
25019         }
25020         
25021         if(o.metaData){
25022             
25023             delete this.ef;
25024             this.metaFromRemote = true;
25025             this.meta = o.metaData;
25026             this.recordType = Roo.data.Record.create(o.metaData.fields);
25027             this.onMetaChange(this.meta, this.recordType, o);
25028         }
25029         return this.readRecords(o);
25030     },
25031
25032     // private function a store will implement
25033     onMetaChange : function(meta, recordType, o){
25034
25035     },
25036
25037     /**
25038          * @ignore
25039          */
25040     simpleAccess: function(obj, subsc) {
25041         return obj[subsc];
25042     },
25043
25044         /**
25045          * @ignore
25046          */
25047     getJsonAccessor: function(){
25048         var re = /[\[\.]/;
25049         return function(expr) {
25050             try {
25051                 return(re.test(expr))
25052                     ? new Function("obj", "return obj." + expr)
25053                     : function(obj){
25054                         return obj[expr];
25055                     };
25056             } catch(e){}
25057             return Roo.emptyFn;
25058         };
25059     }(),
25060
25061     /**
25062      * Create a data block containing Roo.data.Records from an XML document.
25063      * @param {Object} o An object which contains an Array of row objects in the property specified
25064      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25065      * which contains the total size of the dataset.
25066      * @return {Object} data A data block which is used by an Roo.data.Store object as
25067      * a cache of Roo.data.Records.
25068      */
25069     readRecords : function(o){
25070         /**
25071          * After any data loads, the raw JSON data is available for further custom processing.
25072          * @type Object
25073          */
25074         this.o = o;
25075         var s = this.meta, Record = this.recordType,
25076             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25077
25078 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25079         if (!this.ef) {
25080             if(s.totalProperty) {
25081                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25082                 }
25083                 if(s.successProperty) {
25084                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25085                 }
25086                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25087                 if (s.id) {
25088                         var g = this.getJsonAccessor(s.id);
25089                         this.getId = function(rec) {
25090                                 var r = g(rec);  
25091                                 return (r === undefined || r === "") ? null : r;
25092                         };
25093                 } else {
25094                         this.getId = function(){return null;};
25095                 }
25096             this.ef = [];
25097             for(var jj = 0; jj < fl; jj++){
25098                 f = fi[jj];
25099                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25100                 this.ef[jj] = this.getJsonAccessor(map);
25101             }
25102         }
25103
25104         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25105         if(s.totalProperty){
25106             var vt = parseInt(this.getTotal(o), 10);
25107             if(!isNaN(vt)){
25108                 totalRecords = vt;
25109             }
25110         }
25111         if(s.successProperty){
25112             var vs = this.getSuccess(o);
25113             if(vs === false || vs === 'false'){
25114                 success = false;
25115             }
25116         }
25117         var records = [];
25118         for(var i = 0; i < c; i++){
25119                 var n = root[i];
25120             var values = {};
25121             var id = this.getId(n);
25122             for(var j = 0; j < fl; j++){
25123                 f = fi[j];
25124             var v = this.ef[j](n);
25125             if (!f.convert) {
25126                 Roo.log('missing convert for ' + f.name);
25127                 Roo.log(f);
25128                 continue;
25129             }
25130             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25131             }
25132             var record = new Record(values, id);
25133             record.json = n;
25134             records[i] = record;
25135         }
25136         return {
25137             raw : o,
25138             success : success,
25139             records : records,
25140             totalRecords : totalRecords
25141         };
25142     },
25143     // used when loading children.. @see loadDataFromChildren
25144     toLoadData: function(rec)
25145     {
25146         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25147         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25148         return { data : data, total : data.length };
25149         
25150     }
25151 });/*
25152  * Based on:
25153  * Ext JS Library 1.1.1
25154  * Copyright(c) 2006-2007, Ext JS, LLC.
25155  *
25156  * Originally Released Under LGPL - original licence link has changed is not relivant.
25157  *
25158  * Fork - LGPL
25159  * <script type="text/javascript">
25160  */
25161
25162 /**
25163  * @class Roo.data.XmlReader
25164  * @extends Roo.data.DataReader
25165  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25166  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25167  * <p>
25168  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25169  * header in the HTTP response must be set to "text/xml".</em>
25170  * <p>
25171  * Example code:
25172  * <pre><code>
25173 var RecordDef = Roo.data.Record.create([
25174    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25175    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25176 ]);
25177 var myReader = new Roo.data.XmlReader({
25178    totalRecords: "results", // The element which contains the total dataset size (optional)
25179    record: "row",           // The repeated element which contains row information
25180    id: "id"                 // The element within the row that provides an ID for the record (optional)
25181 }, RecordDef);
25182 </code></pre>
25183  * <p>
25184  * This would consume an XML file like this:
25185  * <pre><code>
25186 &lt;?xml?>
25187 &lt;dataset>
25188  &lt;results>2&lt;/results>
25189  &lt;row>
25190    &lt;id>1&lt;/id>
25191    &lt;name>Bill&lt;/name>
25192    &lt;occupation>Gardener&lt;/occupation>
25193  &lt;/row>
25194  &lt;row>
25195    &lt;id>2&lt;/id>
25196    &lt;name>Ben&lt;/name>
25197    &lt;occupation>Horticulturalist&lt;/occupation>
25198  &lt;/row>
25199 &lt;/dataset>
25200 </code></pre>
25201  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25202  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25203  * paged from the remote server.
25204  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25205  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25206  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25207  * a record identifier value.
25208  * @constructor
25209  * Create a new XmlReader
25210  * @param {Object} meta Metadata configuration options
25211  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25212  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25213  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25214  */
25215 Roo.data.XmlReader = function(meta, recordType){
25216     meta = meta || {};
25217     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25218 };
25219 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25220     
25221     readerType : 'Xml',
25222     
25223     /**
25224      * This method is only used by a DataProxy which has retrieved data from a remote server.
25225          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25226          * to contain a method called 'responseXML' that returns an XML document object.
25227      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25228      * a cache of Roo.data.Records.
25229      */
25230     read : function(response){
25231         var doc = response.responseXML;
25232         if(!doc) {
25233             throw {message: "XmlReader.read: XML Document not available"};
25234         }
25235         return this.readRecords(doc);
25236     },
25237
25238     /**
25239      * Create a data block containing Roo.data.Records from an XML document.
25240          * @param {Object} doc A parsed XML document.
25241      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25242      * a cache of Roo.data.Records.
25243      */
25244     readRecords : function(doc){
25245         /**
25246          * After any data loads/reads, the raw XML Document is available for further custom processing.
25247          * @type XMLDocument
25248          */
25249         this.xmlData = doc;
25250         var root = doc.documentElement || doc;
25251         var q = Roo.DomQuery;
25252         var recordType = this.recordType, fields = recordType.prototype.fields;
25253         var sid = this.meta.id;
25254         var totalRecords = 0, success = true;
25255         if(this.meta.totalRecords){
25256             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25257         }
25258         
25259         if(this.meta.success){
25260             var sv = q.selectValue(this.meta.success, root, true);
25261             success = sv !== false && sv !== 'false';
25262         }
25263         var records = [];
25264         var ns = q.select(this.meta.record, root);
25265         for(var i = 0, len = ns.length; i < len; i++) {
25266                 var n = ns[i];
25267                 var values = {};
25268                 var id = sid ? q.selectValue(sid, n) : undefined;
25269                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25270                     var f = fields.items[j];
25271                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25272                     v = f.convert(v);
25273                     values[f.name] = v;
25274                 }
25275                 var record = new recordType(values, id);
25276                 record.node = n;
25277                 records[records.length] = record;
25278             }
25279
25280             return {
25281                 success : success,
25282                 records : records,
25283                 totalRecords : totalRecords || records.length
25284             };
25285     }
25286 });/*
25287  * Based on:
25288  * Ext JS Library 1.1.1
25289  * Copyright(c) 2006-2007, Ext JS, LLC.
25290  *
25291  * Originally Released Under LGPL - original licence link has changed is not relivant.
25292  *
25293  * Fork - LGPL
25294  * <script type="text/javascript">
25295  */
25296
25297 /**
25298  * @class Roo.data.ArrayReader
25299  * @extends Roo.data.DataReader
25300  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25301  * Each element of that Array represents a row of data fields. The
25302  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25303  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25304  * <p>
25305  * Example code:.
25306  * <pre><code>
25307 var RecordDef = Roo.data.Record.create([
25308     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25309     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25310 ]);
25311 var myReader = new Roo.data.ArrayReader({
25312     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25313 }, RecordDef);
25314 </code></pre>
25315  * <p>
25316  * This would consume an Array like this:
25317  * <pre><code>
25318 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25319   </code></pre>
25320  
25321  * @constructor
25322  * Create a new JsonReader
25323  * @param {Object} meta Metadata configuration options.
25324  * @param {Object|Array} recordType Either an Array of field definition objects
25325  * 
25326  * @cfg {Array} fields Array of field definition objects
25327  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25328  * as specified to {@link Roo.data.Record#create},
25329  * or an {@link Roo.data.Record} object
25330  *
25331  * 
25332  * created using {@link Roo.data.Record#create}.
25333  */
25334 Roo.data.ArrayReader = function(meta, recordType)
25335 {    
25336     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25337 };
25338
25339 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25340     
25341       /**
25342      * Create a data block containing Roo.data.Records from an XML document.
25343      * @param {Object} o An Array of row objects which represents the dataset.
25344      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25345      * a cache of Roo.data.Records.
25346      */
25347     readRecords : function(o)
25348     {
25349         var sid = this.meta ? this.meta.id : null;
25350         var recordType = this.recordType, fields = recordType.prototype.fields;
25351         var records = [];
25352         var root = o;
25353         for(var i = 0; i < root.length; i++){
25354             var n = root[i];
25355             var values = {};
25356             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25357             for(var j = 0, jlen = fields.length; j < jlen; j++){
25358                 var f = fields.items[j];
25359                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25360                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25361                 v = f.convert(v);
25362                 values[f.name] = v;
25363             }
25364             var record = new recordType(values, id);
25365             record.json = n;
25366             records[records.length] = record;
25367         }
25368         return {
25369             records : records,
25370             totalRecords : records.length
25371         };
25372     },
25373     // used when loading children.. @see loadDataFromChildren
25374     toLoadData: function(rec)
25375     {
25376         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25377         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25378         
25379     }
25380     
25381     
25382 });/*
25383  * Based on:
25384  * Ext JS Library 1.1.1
25385  * Copyright(c) 2006-2007, Ext JS, LLC.
25386  *
25387  * Originally Released Under LGPL - original licence link has changed is not relivant.
25388  *
25389  * Fork - LGPL
25390  * <script type="text/javascript">
25391  */
25392
25393
25394 /**
25395  * @class Roo.data.Tree
25396  * @extends Roo.util.Observable
25397  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25398  * in the tree have most standard DOM functionality.
25399  * @constructor
25400  * @param {Node} root (optional) The root node
25401  */
25402 Roo.data.Tree = function(root){
25403    this.nodeHash = {};
25404    /**
25405     * The root node for this tree
25406     * @type Node
25407     */
25408    this.root = null;
25409    if(root){
25410        this.setRootNode(root);
25411    }
25412    this.addEvents({
25413        /**
25414         * @event append
25415         * Fires when a new child node is appended to a node in this tree.
25416         * @param {Tree} tree The owner tree
25417         * @param {Node} parent The parent node
25418         * @param {Node} node The newly appended node
25419         * @param {Number} index The index of the newly appended node
25420         */
25421        "append" : true,
25422        /**
25423         * @event remove
25424         * Fires when a child node is removed from a node in this tree.
25425         * @param {Tree} tree The owner tree
25426         * @param {Node} parent The parent node
25427         * @param {Node} node The child node removed
25428         */
25429        "remove" : true,
25430        /**
25431         * @event move
25432         * Fires when a node is moved to a new location in the tree
25433         * @param {Tree} tree The owner tree
25434         * @param {Node} node The node moved
25435         * @param {Node} oldParent The old parent of this node
25436         * @param {Node} newParent The new parent of this node
25437         * @param {Number} index The index it was moved to
25438         */
25439        "move" : true,
25440        /**
25441         * @event insert
25442         * Fires when a new child node is inserted in a node in this tree.
25443         * @param {Tree} tree The owner tree
25444         * @param {Node} parent The parent node
25445         * @param {Node} node The child node inserted
25446         * @param {Node} refNode The child node the node was inserted before
25447         */
25448        "insert" : true,
25449        /**
25450         * @event beforeappend
25451         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25452         * @param {Tree} tree The owner tree
25453         * @param {Node} parent The parent node
25454         * @param {Node} node The child node to be appended
25455         */
25456        "beforeappend" : true,
25457        /**
25458         * @event beforeremove
25459         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25460         * @param {Tree} tree The owner tree
25461         * @param {Node} parent The parent node
25462         * @param {Node} node The child node to be removed
25463         */
25464        "beforeremove" : true,
25465        /**
25466         * @event beforemove
25467         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25468         * @param {Tree} tree The owner tree
25469         * @param {Node} node The node being moved
25470         * @param {Node} oldParent The parent of the node
25471         * @param {Node} newParent The new parent the node is moving to
25472         * @param {Number} index The index it is being moved to
25473         */
25474        "beforemove" : true,
25475        /**
25476         * @event beforeinsert
25477         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25478         * @param {Tree} tree The owner tree
25479         * @param {Node} parent The parent node
25480         * @param {Node} node The child node to be inserted
25481         * @param {Node} refNode The child node the node is being inserted before
25482         */
25483        "beforeinsert" : true
25484    });
25485
25486     Roo.data.Tree.superclass.constructor.call(this);
25487 };
25488
25489 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25490     pathSeparator: "/",
25491
25492     proxyNodeEvent : function(){
25493         return this.fireEvent.apply(this, arguments);
25494     },
25495
25496     /**
25497      * Returns the root node for this tree.
25498      * @return {Node}
25499      */
25500     getRootNode : function(){
25501         return this.root;
25502     },
25503
25504     /**
25505      * Sets the root node for this tree.
25506      * @param {Node} node
25507      * @return {Node}
25508      */
25509     setRootNode : function(node){
25510         this.root = node;
25511         node.ownerTree = this;
25512         node.isRoot = true;
25513         this.registerNode(node);
25514         return node;
25515     },
25516
25517     /**
25518      * Gets a node in this tree by its id.
25519      * @param {String} id
25520      * @return {Node}
25521      */
25522     getNodeById : function(id){
25523         return this.nodeHash[id];
25524     },
25525
25526     registerNode : function(node){
25527         this.nodeHash[node.id] = node;
25528     },
25529
25530     unregisterNode : function(node){
25531         delete this.nodeHash[node.id];
25532     },
25533
25534     toString : function(){
25535         return "[Tree"+(this.id?" "+this.id:"")+"]";
25536     }
25537 });
25538
25539 /**
25540  * @class Roo.data.Node
25541  * @extends Roo.util.Observable
25542  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25543  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25544  * @constructor
25545  * @param {Object} attributes The attributes/config for the node
25546  */
25547 Roo.data.Node = function(attributes){
25548     /**
25549      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25550      * @type {Object}
25551      */
25552     this.attributes = attributes || {};
25553     this.leaf = this.attributes.leaf;
25554     /**
25555      * The node id. @type String
25556      */
25557     this.id = this.attributes.id;
25558     if(!this.id){
25559         this.id = Roo.id(null, "ynode-");
25560         this.attributes.id = this.id;
25561     }
25562      
25563     
25564     /**
25565      * All child nodes of this node. @type Array
25566      */
25567     this.childNodes = [];
25568     if(!this.childNodes.indexOf){ // indexOf is a must
25569         this.childNodes.indexOf = function(o){
25570             for(var i = 0, len = this.length; i < len; i++){
25571                 if(this[i] == o) {
25572                     return i;
25573                 }
25574             }
25575             return -1;
25576         };
25577     }
25578     /**
25579      * The parent node for this node. @type Node
25580      */
25581     this.parentNode = null;
25582     /**
25583      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25584      */
25585     this.firstChild = null;
25586     /**
25587      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25588      */
25589     this.lastChild = null;
25590     /**
25591      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25592      */
25593     this.previousSibling = null;
25594     /**
25595      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25596      */
25597     this.nextSibling = null;
25598
25599     this.addEvents({
25600        /**
25601         * @event append
25602         * Fires when a new child node is appended
25603         * @param {Tree} tree The owner tree
25604         * @param {Node} this This node
25605         * @param {Node} node The newly appended node
25606         * @param {Number} index The index of the newly appended node
25607         */
25608        "append" : true,
25609        /**
25610         * @event remove
25611         * Fires when a child node is removed
25612         * @param {Tree} tree The owner tree
25613         * @param {Node} this This node
25614         * @param {Node} node The removed node
25615         */
25616        "remove" : true,
25617        /**
25618         * @event move
25619         * Fires when this node is moved to a new location in the tree
25620         * @param {Tree} tree The owner tree
25621         * @param {Node} this This node
25622         * @param {Node} oldParent The old parent of this node
25623         * @param {Node} newParent The new parent of this node
25624         * @param {Number} index The index it was moved to
25625         */
25626        "move" : true,
25627        /**
25628         * @event insert
25629         * Fires when a new child node is inserted.
25630         * @param {Tree} tree The owner tree
25631         * @param {Node} this This node
25632         * @param {Node} node The child node inserted
25633         * @param {Node} refNode The child node the node was inserted before
25634         */
25635        "insert" : true,
25636        /**
25637         * @event beforeappend
25638         * Fires before a new child is appended, return false to cancel the append.
25639         * @param {Tree} tree The owner tree
25640         * @param {Node} this This node
25641         * @param {Node} node The child node to be appended
25642         */
25643        "beforeappend" : true,
25644        /**
25645         * @event beforeremove
25646         * Fires before a child is removed, return false to cancel the remove.
25647         * @param {Tree} tree The owner tree
25648         * @param {Node} this This node
25649         * @param {Node} node The child node to be removed
25650         */
25651        "beforeremove" : true,
25652        /**
25653         * @event beforemove
25654         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25655         * @param {Tree} tree The owner tree
25656         * @param {Node} this This node
25657         * @param {Node} oldParent The parent of this node
25658         * @param {Node} newParent The new parent this node is moving to
25659         * @param {Number} index The index it is being moved to
25660         */
25661        "beforemove" : true,
25662        /**
25663         * @event beforeinsert
25664         * Fires before a new child is inserted, return false to cancel the insert.
25665         * @param {Tree} tree The owner tree
25666         * @param {Node} this This node
25667         * @param {Node} node The child node to be inserted
25668         * @param {Node} refNode The child node the node is being inserted before
25669         */
25670        "beforeinsert" : true
25671    });
25672     this.listeners = this.attributes.listeners;
25673     Roo.data.Node.superclass.constructor.call(this);
25674 };
25675
25676 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25677     fireEvent : function(evtName){
25678         // first do standard event for this node
25679         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25680             return false;
25681         }
25682         // then bubble it up to the tree if the event wasn't cancelled
25683         var ot = this.getOwnerTree();
25684         if(ot){
25685             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25686                 return false;
25687             }
25688         }
25689         return true;
25690     },
25691
25692     /**
25693      * Returns true if this node is a leaf
25694      * @return {Boolean}
25695      */
25696     isLeaf : function(){
25697         return this.leaf === true;
25698     },
25699
25700     // private
25701     setFirstChild : function(node){
25702         this.firstChild = node;
25703     },
25704
25705     //private
25706     setLastChild : function(node){
25707         this.lastChild = node;
25708     },
25709
25710
25711     /**
25712      * Returns true if this node is the last child of its parent
25713      * @return {Boolean}
25714      */
25715     isLast : function(){
25716        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25717     },
25718
25719     /**
25720      * Returns true if this node is the first child of its parent
25721      * @return {Boolean}
25722      */
25723     isFirst : function(){
25724        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25725     },
25726
25727     hasChildNodes : function(){
25728         return !this.isLeaf() && this.childNodes.length > 0;
25729     },
25730
25731     /**
25732      * Insert node(s) as the last child node of this node.
25733      * @param {Node/Array} node The node or Array of nodes to append
25734      * @return {Node} The appended node if single append, or null if an array was passed
25735      */
25736     appendChild : function(node){
25737         var multi = false;
25738         if(node instanceof Array){
25739             multi = node;
25740         }else if(arguments.length > 1){
25741             multi = arguments;
25742         }
25743         
25744         // if passed an array or multiple args do them one by one
25745         if(multi){
25746             for(var i = 0, len = multi.length; i < len; i++) {
25747                 this.appendChild(multi[i]);
25748             }
25749         }else{
25750             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25751                 return false;
25752             }
25753             var index = this.childNodes.length;
25754             var oldParent = node.parentNode;
25755             // it's a move, make sure we move it cleanly
25756             if(oldParent){
25757                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25758                     return false;
25759                 }
25760                 oldParent.removeChild(node);
25761             }
25762             
25763             index = this.childNodes.length;
25764             if(index == 0){
25765                 this.setFirstChild(node);
25766             }
25767             this.childNodes.push(node);
25768             node.parentNode = this;
25769             var ps = this.childNodes[index-1];
25770             if(ps){
25771                 node.previousSibling = ps;
25772                 ps.nextSibling = node;
25773             }else{
25774                 node.previousSibling = null;
25775             }
25776             node.nextSibling = null;
25777             this.setLastChild(node);
25778             node.setOwnerTree(this.getOwnerTree());
25779             this.fireEvent("append", this.ownerTree, this, node, index);
25780             if(this.ownerTree) {
25781                 this.ownerTree.fireEvent("appendnode", this, node, index);
25782             }
25783             if(oldParent){
25784                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25785             }
25786             return node;
25787         }
25788     },
25789
25790     /**
25791      * Removes a child node from this node.
25792      * @param {Node} node The node to remove
25793      * @return {Node} The removed node
25794      */
25795     removeChild : function(node){
25796         var index = this.childNodes.indexOf(node);
25797         if(index == -1){
25798             return false;
25799         }
25800         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25801             return false;
25802         }
25803
25804         // remove it from childNodes collection
25805         this.childNodes.splice(index, 1);
25806
25807         // update siblings
25808         if(node.previousSibling){
25809             node.previousSibling.nextSibling = node.nextSibling;
25810         }
25811         if(node.nextSibling){
25812             node.nextSibling.previousSibling = node.previousSibling;
25813         }
25814
25815         // update child refs
25816         if(this.firstChild == node){
25817             this.setFirstChild(node.nextSibling);
25818         }
25819         if(this.lastChild == node){
25820             this.setLastChild(node.previousSibling);
25821         }
25822
25823         node.setOwnerTree(null);
25824         // clear any references from the node
25825         node.parentNode = null;
25826         node.previousSibling = null;
25827         node.nextSibling = null;
25828         this.fireEvent("remove", this.ownerTree, this, node);
25829         return node;
25830     },
25831
25832     /**
25833      * Inserts the first node before the second node in this nodes childNodes collection.
25834      * @param {Node} node The node to insert
25835      * @param {Node} refNode The node to insert before (if null the node is appended)
25836      * @return {Node} The inserted node
25837      */
25838     insertBefore : function(node, refNode){
25839         if(!refNode){ // like standard Dom, refNode can be null for append
25840             return this.appendChild(node);
25841         }
25842         // nothing to do
25843         if(node == refNode){
25844             return false;
25845         }
25846
25847         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25848             return false;
25849         }
25850         var index = this.childNodes.indexOf(refNode);
25851         var oldParent = node.parentNode;
25852         var refIndex = index;
25853
25854         // when moving internally, indexes will change after remove
25855         if(oldParent == this && this.childNodes.indexOf(node) < index){
25856             refIndex--;
25857         }
25858
25859         // it's a move, make sure we move it cleanly
25860         if(oldParent){
25861             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25862                 return false;
25863             }
25864             oldParent.removeChild(node);
25865         }
25866         if(refIndex == 0){
25867             this.setFirstChild(node);
25868         }
25869         this.childNodes.splice(refIndex, 0, node);
25870         node.parentNode = this;
25871         var ps = this.childNodes[refIndex-1];
25872         if(ps){
25873             node.previousSibling = ps;
25874             ps.nextSibling = node;
25875         }else{
25876             node.previousSibling = null;
25877         }
25878         node.nextSibling = refNode;
25879         refNode.previousSibling = node;
25880         node.setOwnerTree(this.getOwnerTree());
25881         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25882         if(oldParent){
25883             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25884         }
25885         return node;
25886     },
25887
25888     /**
25889      * Returns the child node at the specified index.
25890      * @param {Number} index
25891      * @return {Node}
25892      */
25893     item : function(index){
25894         return this.childNodes[index];
25895     },
25896
25897     /**
25898      * Replaces one child node in this node with another.
25899      * @param {Node} newChild The replacement node
25900      * @param {Node} oldChild The node to replace
25901      * @return {Node} The replaced node
25902      */
25903     replaceChild : function(newChild, oldChild){
25904         this.insertBefore(newChild, oldChild);
25905         this.removeChild(oldChild);
25906         return oldChild;
25907     },
25908
25909     /**
25910      * Returns the index of a child node
25911      * @param {Node} node
25912      * @return {Number} The index of the node or -1 if it was not found
25913      */
25914     indexOf : function(child){
25915         return this.childNodes.indexOf(child);
25916     },
25917
25918     /**
25919      * Returns the tree this node is in.
25920      * @return {Tree}
25921      */
25922     getOwnerTree : function(){
25923         // if it doesn't have one, look for one
25924         if(!this.ownerTree){
25925             var p = this;
25926             while(p){
25927                 if(p.ownerTree){
25928                     this.ownerTree = p.ownerTree;
25929                     break;
25930                 }
25931                 p = p.parentNode;
25932             }
25933         }
25934         return this.ownerTree;
25935     },
25936
25937     /**
25938      * Returns depth of this node (the root node has a depth of 0)
25939      * @return {Number}
25940      */
25941     getDepth : function(){
25942         var depth = 0;
25943         var p = this;
25944         while(p.parentNode){
25945             ++depth;
25946             p = p.parentNode;
25947         }
25948         return depth;
25949     },
25950
25951     // private
25952     setOwnerTree : function(tree){
25953         // if it's move, we need to update everyone
25954         if(tree != this.ownerTree){
25955             if(this.ownerTree){
25956                 this.ownerTree.unregisterNode(this);
25957             }
25958             this.ownerTree = tree;
25959             var cs = this.childNodes;
25960             for(var i = 0, len = cs.length; i < len; i++) {
25961                 cs[i].setOwnerTree(tree);
25962             }
25963             if(tree){
25964                 tree.registerNode(this);
25965             }
25966         }
25967     },
25968
25969     /**
25970      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25971      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25972      * @return {String} The path
25973      */
25974     getPath : function(attr){
25975         attr = attr || "id";
25976         var p = this.parentNode;
25977         var b = [this.attributes[attr]];
25978         while(p){
25979             b.unshift(p.attributes[attr]);
25980             p = p.parentNode;
25981         }
25982         var sep = this.getOwnerTree().pathSeparator;
25983         return sep + b.join(sep);
25984     },
25985
25986     /**
25987      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25988      * function call will be the scope provided or the current node. The arguments to the function
25989      * will be the args provided or the current node. If the function returns false at any point,
25990      * the bubble is stopped.
25991      * @param {Function} fn The function to call
25992      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25993      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25994      */
25995     bubble : function(fn, scope, args){
25996         var p = this;
25997         while(p){
25998             if(fn.call(scope || p, args || p) === false){
25999                 break;
26000             }
26001             p = p.parentNode;
26002         }
26003     },
26004
26005     /**
26006      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26007      * function call will be the scope provided or the current node. The arguments to the function
26008      * will be the args provided or the current node. If the function returns false at any point,
26009      * the cascade is stopped on that branch.
26010      * @param {Function} fn The function to call
26011      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26012      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26013      */
26014     cascade : function(fn, scope, args){
26015         if(fn.call(scope || this, args || this) !== false){
26016             var cs = this.childNodes;
26017             for(var i = 0, len = cs.length; i < len; i++) {
26018                 cs[i].cascade(fn, scope, args);
26019             }
26020         }
26021     },
26022
26023     /**
26024      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26025      * function call will be the scope provided or the current node. The arguments to the function
26026      * will be the args provided or the current node. If the function returns false at any point,
26027      * the iteration stops.
26028      * @param {Function} fn The function to call
26029      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26030      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26031      */
26032     eachChild : function(fn, scope, args){
26033         var cs = this.childNodes;
26034         for(var i = 0, len = cs.length; i < len; i++) {
26035                 if(fn.call(scope || this, args || cs[i]) === false){
26036                     break;
26037                 }
26038         }
26039     },
26040
26041     /**
26042      * Finds the first child that has the attribute with the specified value.
26043      * @param {String} attribute The attribute name
26044      * @param {Mixed} value The value to search for
26045      * @return {Node} The found child or null if none was found
26046      */
26047     findChild : function(attribute, value){
26048         var cs = this.childNodes;
26049         for(var i = 0, len = cs.length; i < len; i++) {
26050                 if(cs[i].attributes[attribute] == value){
26051                     return cs[i];
26052                 }
26053         }
26054         return null;
26055     },
26056
26057     /**
26058      * Finds the first child by a custom function. The child matches if the function passed
26059      * returns true.
26060      * @param {Function} fn
26061      * @param {Object} scope (optional)
26062      * @return {Node} The found child or null if none was found
26063      */
26064     findChildBy : function(fn, scope){
26065         var cs = this.childNodes;
26066         for(var i = 0, len = cs.length; i < len; i++) {
26067                 if(fn.call(scope||cs[i], cs[i]) === true){
26068                     return cs[i];
26069                 }
26070         }
26071         return null;
26072     },
26073
26074     /**
26075      * Sorts this nodes children using the supplied sort function
26076      * @param {Function} fn
26077      * @param {Object} scope (optional)
26078      */
26079     sort : function(fn, scope){
26080         var cs = this.childNodes;
26081         var len = cs.length;
26082         if(len > 0){
26083             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26084             cs.sort(sortFn);
26085             for(var i = 0; i < len; i++){
26086                 var n = cs[i];
26087                 n.previousSibling = cs[i-1];
26088                 n.nextSibling = cs[i+1];
26089                 if(i == 0){
26090                     this.setFirstChild(n);
26091                 }
26092                 if(i == len-1){
26093                     this.setLastChild(n);
26094                 }
26095             }
26096         }
26097     },
26098
26099     /**
26100      * Returns true if this node is an ancestor (at any point) of the passed node.
26101      * @param {Node} node
26102      * @return {Boolean}
26103      */
26104     contains : function(node){
26105         return node.isAncestor(this);
26106     },
26107
26108     /**
26109      * Returns true if the passed node is an ancestor (at any point) of this node.
26110      * @param {Node} node
26111      * @return {Boolean}
26112      */
26113     isAncestor : function(node){
26114         var p = this.parentNode;
26115         while(p){
26116             if(p == node){
26117                 return true;
26118             }
26119             p = p.parentNode;
26120         }
26121         return false;
26122     },
26123
26124     toString : function(){
26125         return "[Node"+(this.id?" "+this.id:"")+"]";
26126     }
26127 });/*
26128  * Based on:
26129  * Ext JS Library 1.1.1
26130  * Copyright(c) 2006-2007, Ext JS, LLC.
26131  *
26132  * Originally Released Under LGPL - original licence link has changed is not relivant.
26133  *
26134  * Fork - LGPL
26135  * <script type="text/javascript">
26136  */
26137
26138
26139 /**
26140  * @class Roo.Shadow
26141  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26142  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26143  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26144  * @constructor
26145  * Create a new Shadow
26146  * @param {Object} config The config object
26147  */
26148 Roo.Shadow = function(config){
26149     Roo.apply(this, config);
26150     if(typeof this.mode != "string"){
26151         this.mode = this.defaultMode;
26152     }
26153     var o = this.offset, a = {h: 0};
26154     var rad = Math.floor(this.offset/2);
26155     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26156         case "drop":
26157             a.w = 0;
26158             a.l = a.t = o;
26159             a.t -= 1;
26160             if(Roo.isIE){
26161                 a.l -= this.offset + rad;
26162                 a.t -= this.offset + rad;
26163                 a.w -= rad;
26164                 a.h -= rad;
26165                 a.t += 1;
26166             }
26167         break;
26168         case "sides":
26169             a.w = (o*2);
26170             a.l = -o;
26171             a.t = o-1;
26172             if(Roo.isIE){
26173                 a.l -= (this.offset - rad);
26174                 a.t -= this.offset + rad;
26175                 a.l += 1;
26176                 a.w -= (this.offset - rad)*2;
26177                 a.w -= rad + 1;
26178                 a.h -= 1;
26179             }
26180         break;
26181         case "frame":
26182             a.w = a.h = (o*2);
26183             a.l = a.t = -o;
26184             a.t += 1;
26185             a.h -= 2;
26186             if(Roo.isIE){
26187                 a.l -= (this.offset - rad);
26188                 a.t -= (this.offset - rad);
26189                 a.l += 1;
26190                 a.w -= (this.offset + rad + 1);
26191                 a.h -= (this.offset + rad);
26192                 a.h += 1;
26193             }
26194         break;
26195     };
26196
26197     this.adjusts = a;
26198 };
26199
26200 Roo.Shadow.prototype = {
26201     /**
26202      * @cfg {String} mode
26203      * The shadow display mode.  Supports the following options:<br />
26204      * sides: Shadow displays on both sides and bottom only<br />
26205      * frame: Shadow displays equally on all four sides<br />
26206      * drop: Traditional bottom-right drop shadow (default)
26207      */
26208     /**
26209      * @cfg {String} offset
26210      * The number of pixels to offset the shadow from the element (defaults to 4)
26211      */
26212     offset: 4,
26213
26214     // private
26215     defaultMode: "drop",
26216
26217     /**
26218      * Displays the shadow under the target element
26219      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26220      */
26221     show : function(target){
26222         target = Roo.get(target);
26223         if(!this.el){
26224             this.el = Roo.Shadow.Pool.pull();
26225             if(this.el.dom.nextSibling != target.dom){
26226                 this.el.insertBefore(target);
26227             }
26228         }
26229         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26230         if(Roo.isIE){
26231             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26232         }
26233         this.realign(
26234             target.getLeft(true),
26235             target.getTop(true),
26236             target.getWidth(),
26237             target.getHeight()
26238         );
26239         this.el.dom.style.display = "block";
26240     },
26241
26242     /**
26243      * Returns true if the shadow is visible, else false
26244      */
26245     isVisible : function(){
26246         return this.el ? true : false;  
26247     },
26248
26249     /**
26250      * Direct alignment when values are already available. Show must be called at least once before
26251      * calling this method to ensure it is initialized.
26252      * @param {Number} left The target element left position
26253      * @param {Number} top The target element top position
26254      * @param {Number} width The target element width
26255      * @param {Number} height The target element height
26256      */
26257     realign : function(l, t, w, h){
26258         if(!this.el){
26259             return;
26260         }
26261         var a = this.adjusts, d = this.el.dom, s = d.style;
26262         var iea = 0;
26263         s.left = (l+a.l)+"px";
26264         s.top = (t+a.t)+"px";
26265         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26266  
26267         if(s.width != sws || s.height != shs){
26268             s.width = sws;
26269             s.height = shs;
26270             if(!Roo.isIE){
26271                 var cn = d.childNodes;
26272                 var sww = Math.max(0, (sw-12))+"px";
26273                 cn[0].childNodes[1].style.width = sww;
26274                 cn[1].childNodes[1].style.width = sww;
26275                 cn[2].childNodes[1].style.width = sww;
26276                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26277             }
26278         }
26279     },
26280
26281     /**
26282      * Hides this shadow
26283      */
26284     hide : function(){
26285         if(this.el){
26286             this.el.dom.style.display = "none";
26287             Roo.Shadow.Pool.push(this.el);
26288             delete this.el;
26289         }
26290     },
26291
26292     /**
26293      * Adjust the z-index of this shadow
26294      * @param {Number} zindex The new z-index
26295      */
26296     setZIndex : function(z){
26297         this.zIndex = z;
26298         if(this.el){
26299             this.el.setStyle("z-index", z);
26300         }
26301     }
26302 };
26303
26304 // Private utility class that manages the internal Shadow cache
26305 Roo.Shadow.Pool = function(){
26306     var p = [];
26307     var markup = Roo.isIE ?
26308                  '<div class="x-ie-shadow"></div>' :
26309                  '<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>';
26310     return {
26311         pull : function(){
26312             var sh = p.shift();
26313             if(!sh){
26314                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26315                 sh.autoBoxAdjust = false;
26316             }
26317             return sh;
26318         },
26319
26320         push : function(sh){
26321             p.push(sh);
26322         }
26323     };
26324 }();/*
26325  * Based on:
26326  * Ext JS Library 1.1.1
26327  * Copyright(c) 2006-2007, Ext JS, LLC.
26328  *
26329  * Originally Released Under LGPL - original licence link has changed is not relivant.
26330  *
26331  * Fork - LGPL
26332  * <script type="text/javascript">
26333  */
26334
26335
26336 /**
26337  * @class Roo.SplitBar
26338  * @extends Roo.util.Observable
26339  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26340  * <br><br>
26341  * Usage:
26342  * <pre><code>
26343 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26344                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26345 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26346 split.minSize = 100;
26347 split.maxSize = 600;
26348 split.animate = true;
26349 split.on('moved', splitterMoved);
26350 </code></pre>
26351  * @constructor
26352  * Create a new SplitBar
26353  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26354  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26355  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26356  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26357                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26358                         position of the SplitBar).
26359  */
26360 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26361     
26362     /** @private */
26363     this.el = Roo.get(dragElement, true);
26364     this.el.dom.unselectable = "on";
26365     /** @private */
26366     this.resizingEl = Roo.get(resizingElement, true);
26367
26368     /**
26369      * @private
26370      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26371      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26372      * @type Number
26373      */
26374     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26375     
26376     /**
26377      * The minimum size of the resizing element. (Defaults to 0)
26378      * @type Number
26379      */
26380     this.minSize = 0;
26381     
26382     /**
26383      * The maximum size of the resizing element. (Defaults to 2000)
26384      * @type Number
26385      */
26386     this.maxSize = 2000;
26387     
26388     /**
26389      * Whether to animate the transition to the new size
26390      * @type Boolean
26391      */
26392     this.animate = false;
26393     
26394     /**
26395      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26396      * @type Boolean
26397      */
26398     this.useShim = false;
26399     
26400     /** @private */
26401     this.shim = null;
26402     
26403     if(!existingProxy){
26404         /** @private */
26405         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26406     }else{
26407         this.proxy = Roo.get(existingProxy).dom;
26408     }
26409     /** @private */
26410     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26411     
26412     /** @private */
26413     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26414     
26415     /** @private */
26416     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26417     
26418     /** @private */
26419     this.dragSpecs = {};
26420     
26421     /**
26422      * @private The adapter to use to positon and resize elements
26423      */
26424     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26425     this.adapter.init(this);
26426     
26427     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26428         /** @private */
26429         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26430         this.el.addClass("x-splitbar-h");
26431     }else{
26432         /** @private */
26433         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26434         this.el.addClass("x-splitbar-v");
26435     }
26436     
26437     this.addEvents({
26438         /**
26439          * @event resize
26440          * Fires when the splitter is moved (alias for {@link #event-moved})
26441          * @param {Roo.SplitBar} this
26442          * @param {Number} newSize the new width or height
26443          */
26444         "resize" : true,
26445         /**
26446          * @event moved
26447          * Fires when the splitter is moved
26448          * @param {Roo.SplitBar} this
26449          * @param {Number} newSize the new width or height
26450          */
26451         "moved" : true,
26452         /**
26453          * @event beforeresize
26454          * Fires before the splitter is dragged
26455          * @param {Roo.SplitBar} this
26456          */
26457         "beforeresize" : true,
26458
26459         "beforeapply" : true
26460     });
26461
26462     Roo.util.Observable.call(this);
26463 };
26464
26465 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26466     onStartProxyDrag : function(x, y){
26467         this.fireEvent("beforeresize", this);
26468         if(!this.overlay){
26469             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26470             o.unselectable();
26471             o.enableDisplayMode("block");
26472             // all splitbars share the same overlay
26473             Roo.SplitBar.prototype.overlay = o;
26474         }
26475         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26476         this.overlay.show();
26477         Roo.get(this.proxy).setDisplayed("block");
26478         var size = this.adapter.getElementSize(this);
26479         this.activeMinSize = this.getMinimumSize();;
26480         this.activeMaxSize = this.getMaximumSize();;
26481         var c1 = size - this.activeMinSize;
26482         var c2 = Math.max(this.activeMaxSize - size, 0);
26483         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26484             this.dd.resetConstraints();
26485             this.dd.setXConstraint(
26486                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26487                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26488             );
26489             this.dd.setYConstraint(0, 0);
26490         }else{
26491             this.dd.resetConstraints();
26492             this.dd.setXConstraint(0, 0);
26493             this.dd.setYConstraint(
26494                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26495                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26496             );
26497          }
26498         this.dragSpecs.startSize = size;
26499         this.dragSpecs.startPoint = [x, y];
26500         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26501     },
26502     
26503     /** 
26504      * @private Called after the drag operation by the DDProxy
26505      */
26506     onEndProxyDrag : function(e){
26507         Roo.get(this.proxy).setDisplayed(false);
26508         var endPoint = Roo.lib.Event.getXY(e);
26509         if(this.overlay){
26510             this.overlay.hide();
26511         }
26512         var newSize;
26513         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26514             newSize = this.dragSpecs.startSize + 
26515                 (this.placement == Roo.SplitBar.LEFT ?
26516                     endPoint[0] - this.dragSpecs.startPoint[0] :
26517                     this.dragSpecs.startPoint[0] - endPoint[0]
26518                 );
26519         }else{
26520             newSize = this.dragSpecs.startSize + 
26521                 (this.placement == Roo.SplitBar.TOP ?
26522                     endPoint[1] - this.dragSpecs.startPoint[1] :
26523                     this.dragSpecs.startPoint[1] - endPoint[1]
26524                 );
26525         }
26526         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26527         if(newSize != this.dragSpecs.startSize){
26528             if(this.fireEvent('beforeapply', this, newSize) !== false){
26529                 this.adapter.setElementSize(this, newSize);
26530                 this.fireEvent("moved", this, newSize);
26531                 this.fireEvent("resize", this, newSize);
26532             }
26533         }
26534     },
26535     
26536     /**
26537      * Get the adapter this SplitBar uses
26538      * @return The adapter object
26539      */
26540     getAdapter : function(){
26541         return this.adapter;
26542     },
26543     
26544     /**
26545      * Set the adapter this SplitBar uses
26546      * @param {Object} adapter A SplitBar adapter object
26547      */
26548     setAdapter : function(adapter){
26549         this.adapter = adapter;
26550         this.adapter.init(this);
26551     },
26552     
26553     /**
26554      * Gets the minimum size for the resizing element
26555      * @return {Number} The minimum size
26556      */
26557     getMinimumSize : function(){
26558         return this.minSize;
26559     },
26560     
26561     /**
26562      * Sets the minimum size for the resizing element
26563      * @param {Number} minSize The minimum size
26564      */
26565     setMinimumSize : function(minSize){
26566         this.minSize = minSize;
26567     },
26568     
26569     /**
26570      * Gets the maximum size for the resizing element
26571      * @return {Number} The maximum size
26572      */
26573     getMaximumSize : function(){
26574         return this.maxSize;
26575     },
26576     
26577     /**
26578      * Sets the maximum size for the resizing element
26579      * @param {Number} maxSize The maximum size
26580      */
26581     setMaximumSize : function(maxSize){
26582         this.maxSize = maxSize;
26583     },
26584     
26585     /**
26586      * Sets the initialize size for the resizing element
26587      * @param {Number} size The initial size
26588      */
26589     setCurrentSize : function(size){
26590         var oldAnimate = this.animate;
26591         this.animate = false;
26592         this.adapter.setElementSize(this, size);
26593         this.animate = oldAnimate;
26594     },
26595     
26596     /**
26597      * Destroy this splitbar. 
26598      * @param {Boolean} removeEl True to remove the element
26599      */
26600     destroy : function(removeEl){
26601         if(this.shim){
26602             this.shim.remove();
26603         }
26604         this.dd.unreg();
26605         this.proxy.parentNode.removeChild(this.proxy);
26606         if(removeEl){
26607             this.el.remove();
26608         }
26609     }
26610 });
26611
26612 /**
26613  * @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.
26614  */
26615 Roo.SplitBar.createProxy = function(dir){
26616     var proxy = new Roo.Element(document.createElement("div"));
26617     proxy.unselectable();
26618     var cls = 'x-splitbar-proxy';
26619     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26620     document.body.appendChild(proxy.dom);
26621     return proxy.dom;
26622 };
26623
26624 /** 
26625  * @class Roo.SplitBar.BasicLayoutAdapter
26626  * Default Adapter. It assumes the splitter and resizing element are not positioned
26627  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26628  */
26629 Roo.SplitBar.BasicLayoutAdapter = function(){
26630 };
26631
26632 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26633     // do nothing for now
26634     init : function(s){
26635     
26636     },
26637     /**
26638      * Called before drag operations to get the current size of the resizing element. 
26639      * @param {Roo.SplitBar} s The SplitBar using this adapter
26640      */
26641      getElementSize : function(s){
26642         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26643             return s.resizingEl.getWidth();
26644         }else{
26645             return s.resizingEl.getHeight();
26646         }
26647     },
26648     
26649     /**
26650      * Called after drag operations to set the size of the resizing element.
26651      * @param {Roo.SplitBar} s The SplitBar using this adapter
26652      * @param {Number} newSize The new size to set
26653      * @param {Function} onComplete A function to be invoked when resizing is complete
26654      */
26655     setElementSize : function(s, newSize, onComplete){
26656         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26657             if(!s.animate){
26658                 s.resizingEl.setWidth(newSize);
26659                 if(onComplete){
26660                     onComplete(s, newSize);
26661                 }
26662             }else{
26663                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26664             }
26665         }else{
26666             
26667             if(!s.animate){
26668                 s.resizingEl.setHeight(newSize);
26669                 if(onComplete){
26670                     onComplete(s, newSize);
26671                 }
26672             }else{
26673                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26674             }
26675         }
26676     }
26677 };
26678
26679 /** 
26680  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26681  * @extends Roo.SplitBar.BasicLayoutAdapter
26682  * Adapter that  moves the splitter element to align with the resized sizing element. 
26683  * Used with an absolute positioned SplitBar.
26684  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26685  * document.body, make sure you assign an id to the body element.
26686  */
26687 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26688     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26689     this.container = Roo.get(container);
26690 };
26691
26692 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26693     init : function(s){
26694         this.basic.init(s);
26695     },
26696     
26697     getElementSize : function(s){
26698         return this.basic.getElementSize(s);
26699     },
26700     
26701     setElementSize : function(s, newSize, onComplete){
26702         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26703     },
26704     
26705     moveSplitter : function(s){
26706         var yes = Roo.SplitBar;
26707         switch(s.placement){
26708             case yes.LEFT:
26709                 s.el.setX(s.resizingEl.getRight());
26710                 break;
26711             case yes.RIGHT:
26712                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26713                 break;
26714             case yes.TOP:
26715                 s.el.setY(s.resizingEl.getBottom());
26716                 break;
26717             case yes.BOTTOM:
26718                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26719                 break;
26720         }
26721     }
26722 };
26723
26724 /**
26725  * Orientation constant - Create a vertical SplitBar
26726  * @static
26727  * @type Number
26728  */
26729 Roo.SplitBar.VERTICAL = 1;
26730
26731 /**
26732  * Orientation constant - Create a horizontal SplitBar
26733  * @static
26734  * @type Number
26735  */
26736 Roo.SplitBar.HORIZONTAL = 2;
26737
26738 /**
26739  * Placement constant - The resizing element is to the left of the splitter element
26740  * @static
26741  * @type Number
26742  */
26743 Roo.SplitBar.LEFT = 1;
26744
26745 /**
26746  * Placement constant - The resizing element is to the right of the splitter element
26747  * @static
26748  * @type Number
26749  */
26750 Roo.SplitBar.RIGHT = 2;
26751
26752 /**
26753  * Placement constant - The resizing element is positioned above the splitter element
26754  * @static
26755  * @type Number
26756  */
26757 Roo.SplitBar.TOP = 3;
26758
26759 /**
26760  * Placement constant - The resizing element is positioned under splitter element
26761  * @static
26762  * @type Number
26763  */
26764 Roo.SplitBar.BOTTOM = 4;
26765 /*
26766  * Based on:
26767  * Ext JS Library 1.1.1
26768  * Copyright(c) 2006-2007, Ext JS, LLC.
26769  *
26770  * Originally Released Under LGPL - original licence link has changed is not relivant.
26771  *
26772  * Fork - LGPL
26773  * <script type="text/javascript">
26774  */
26775
26776 /**
26777  * @class Roo.View
26778  * @extends Roo.util.Observable
26779  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26780  * This class also supports single and multi selection modes. <br>
26781  * Create a data model bound view:
26782  <pre><code>
26783  var store = new Roo.data.Store(...);
26784
26785  var view = new Roo.View({
26786     el : "my-element",
26787     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26788  
26789     singleSelect: true,
26790     selectedClass: "ydataview-selected",
26791     store: store
26792  });
26793
26794  // listen for node click?
26795  view.on("click", function(vw, index, node, e){
26796  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26797  });
26798
26799  // load XML data
26800  dataModel.load("foobar.xml");
26801  </code></pre>
26802  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26803  * <br><br>
26804  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26805  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26806  * 
26807  * Note: old style constructor is still suported (container, template, config)
26808  * 
26809  * @constructor
26810  * Create a new View
26811  * @param {Object} config The config object
26812  * 
26813  */
26814 Roo.View = function(config, depreciated_tpl, depreciated_config){
26815     
26816     this.parent = false;
26817     
26818     if (typeof(depreciated_tpl) == 'undefined') {
26819         // new way.. - universal constructor.
26820         Roo.apply(this, config);
26821         this.el  = Roo.get(this.el);
26822     } else {
26823         // old format..
26824         this.el  = Roo.get(config);
26825         this.tpl = depreciated_tpl;
26826         Roo.apply(this, depreciated_config);
26827     }
26828     this.wrapEl  = this.el.wrap().wrap();
26829     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26830     
26831     
26832     if(typeof(this.tpl) == "string"){
26833         this.tpl = new Roo.Template(this.tpl);
26834     } else {
26835         // support xtype ctors..
26836         this.tpl = new Roo.factory(this.tpl, Roo);
26837     }
26838     
26839     
26840     this.tpl.compile();
26841     
26842     /** @private */
26843     this.addEvents({
26844         /**
26845          * @event beforeclick
26846          * Fires before a click is processed. Returns false to cancel the default action.
26847          * @param {Roo.View} this
26848          * @param {Number} index The index of the target node
26849          * @param {HTMLElement} node The target node
26850          * @param {Roo.EventObject} e The raw event object
26851          */
26852             "beforeclick" : true,
26853         /**
26854          * @event click
26855          * Fires when a template node is clicked.
26856          * @param {Roo.View} this
26857          * @param {Number} index The index of the target node
26858          * @param {HTMLElement} node The target node
26859          * @param {Roo.EventObject} e The raw event object
26860          */
26861             "click" : true,
26862         /**
26863          * @event dblclick
26864          * Fires when a template node is double clicked.
26865          * @param {Roo.View} this
26866          * @param {Number} index The index of the target node
26867          * @param {HTMLElement} node The target node
26868          * @param {Roo.EventObject} e The raw event object
26869          */
26870             "dblclick" : true,
26871         /**
26872          * @event contextmenu
26873          * Fires when a template node is right clicked.
26874          * @param {Roo.View} this
26875          * @param {Number} index The index of the target node
26876          * @param {HTMLElement} node The target node
26877          * @param {Roo.EventObject} e The raw event object
26878          */
26879             "contextmenu" : true,
26880         /**
26881          * @event selectionchange
26882          * Fires when the selected nodes change.
26883          * @param {Roo.View} this
26884          * @param {Array} selections Array of the selected nodes
26885          */
26886             "selectionchange" : true,
26887     
26888         /**
26889          * @event beforeselect
26890          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26891          * @param {Roo.View} this
26892          * @param {HTMLElement} node The node to be selected
26893          * @param {Array} selections Array of currently selected nodes
26894          */
26895             "beforeselect" : true,
26896         /**
26897          * @event preparedata
26898          * Fires on every row to render, to allow you to change the data.
26899          * @param {Roo.View} this
26900          * @param {Object} data to be rendered (change this)
26901          */
26902           "preparedata" : true
26903           
26904           
26905         });
26906
26907
26908
26909     this.el.on({
26910         "click": this.onClick,
26911         "dblclick": this.onDblClick,
26912         "contextmenu": this.onContextMenu,
26913         scope:this
26914     });
26915
26916     this.selections = [];
26917     this.nodes = [];
26918     this.cmp = new Roo.CompositeElementLite([]);
26919     if(this.store){
26920         this.store = Roo.factory(this.store, Roo.data);
26921         this.setStore(this.store, true);
26922     }
26923     
26924     if ( this.footer && this.footer.xtype) {
26925            
26926          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26927         
26928         this.footer.dataSource = this.store;
26929         this.footer.container = fctr;
26930         this.footer = Roo.factory(this.footer, Roo);
26931         fctr.insertFirst(this.el);
26932         
26933         // this is a bit insane - as the paging toolbar seems to detach the el..
26934 //        dom.parentNode.parentNode.parentNode
26935          // they get detached?
26936     }
26937     
26938     
26939     Roo.View.superclass.constructor.call(this);
26940     
26941     
26942 };
26943
26944 Roo.extend(Roo.View, Roo.util.Observable, {
26945     
26946      /**
26947      * @cfg {Roo.data.Store} store Data store to load data from.
26948      */
26949     store : false,
26950     
26951     /**
26952      * @cfg {String|Roo.Element} el The container element.
26953      */
26954     el : '',
26955     
26956     /**
26957      * @cfg {String|Roo.Template} tpl The template used by this View 
26958      */
26959     tpl : false,
26960     /**
26961      * @cfg {String} dataName the named area of the template to use as the data area
26962      *                          Works with domtemplates roo-name="name"
26963      */
26964     dataName: false,
26965     /**
26966      * @cfg {String} selectedClass The css class to add to selected nodes
26967      */
26968     selectedClass : "x-view-selected",
26969      /**
26970      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26971      */
26972     emptyText : "",
26973     
26974     /**
26975      * @cfg {String} text to display on mask (default Loading)
26976      */
26977     mask : false,
26978     /**
26979      * @cfg {Boolean} multiSelect Allow multiple selection
26980      */
26981     multiSelect : false,
26982     /**
26983      * @cfg {Boolean} singleSelect Allow single selection
26984      */
26985     singleSelect:  false,
26986     
26987     /**
26988      * @cfg {Boolean} toggleSelect - selecting 
26989      */
26990     toggleSelect : false,
26991     
26992     /**
26993      * @cfg {Boolean} tickable - selecting 
26994      */
26995     tickable : false,
26996     
26997     /**
26998      * Returns the element this view is bound to.
26999      * @return {Roo.Element}
27000      */
27001     getEl : function(){
27002         return this.wrapEl;
27003     },
27004     
27005     
27006
27007     /**
27008      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27009      */
27010     refresh : function(){
27011         //Roo.log('refresh');
27012         var t = this.tpl;
27013         
27014         // if we are using something like 'domtemplate', then
27015         // the what gets used is:
27016         // t.applySubtemplate(NAME, data, wrapping data..)
27017         // the outer template then get' applied with
27018         //     the store 'extra data'
27019         // and the body get's added to the
27020         //      roo-name="data" node?
27021         //      <span class='roo-tpl-{name}'></span> ?????
27022         
27023         
27024         
27025         this.clearSelections();
27026         this.el.update("");
27027         var html = [];
27028         var records = this.store.getRange();
27029         if(records.length < 1) {
27030             
27031             // is this valid??  = should it render a template??
27032             
27033             this.el.update(this.emptyText);
27034             return;
27035         }
27036         var el = this.el;
27037         if (this.dataName) {
27038             this.el.update(t.apply(this.store.meta)); //????
27039             el = this.el.child('.roo-tpl-' + this.dataName);
27040         }
27041         
27042         for(var i = 0, len = records.length; i < len; i++){
27043             var data = this.prepareData(records[i].data, i, records[i]);
27044             this.fireEvent("preparedata", this, data, i, records[i]);
27045             
27046             var d = Roo.apply({}, data);
27047             
27048             if(this.tickable){
27049                 Roo.apply(d, {'roo-id' : Roo.id()});
27050                 
27051                 var _this = this;
27052             
27053                 Roo.each(this.parent.item, function(item){
27054                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27055                         return;
27056                     }
27057                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27058                 });
27059             }
27060             
27061             html[html.length] = Roo.util.Format.trim(
27062                 this.dataName ?
27063                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27064                     t.apply(d)
27065             );
27066         }
27067         
27068         
27069         
27070         el.update(html.join(""));
27071         this.nodes = el.dom.childNodes;
27072         this.updateIndexes(0);
27073     },
27074     
27075
27076     /**
27077      * Function to override to reformat the data that is sent to
27078      * the template for each node.
27079      * DEPRICATED - use the preparedata event handler.
27080      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27081      * a JSON object for an UpdateManager bound view).
27082      */
27083     prepareData : function(data, index, record)
27084     {
27085         this.fireEvent("preparedata", this, data, index, record);
27086         return data;
27087     },
27088
27089     onUpdate : function(ds, record){
27090         // Roo.log('on update');   
27091         this.clearSelections();
27092         var index = this.store.indexOf(record);
27093         var n = this.nodes[index];
27094         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27095         n.parentNode.removeChild(n);
27096         this.updateIndexes(index, index);
27097     },
27098
27099     
27100     
27101 // --------- FIXME     
27102     onAdd : function(ds, records, index)
27103     {
27104         //Roo.log(['on Add', ds, records, index] );        
27105         this.clearSelections();
27106         if(this.nodes.length == 0){
27107             this.refresh();
27108             return;
27109         }
27110         var n = this.nodes[index];
27111         for(var i = 0, len = records.length; i < len; i++){
27112             var d = this.prepareData(records[i].data, i, records[i]);
27113             if(n){
27114                 this.tpl.insertBefore(n, d);
27115             }else{
27116                 
27117                 this.tpl.append(this.el, d);
27118             }
27119         }
27120         this.updateIndexes(index);
27121     },
27122
27123     onRemove : function(ds, record, index){
27124        // Roo.log('onRemove');
27125         this.clearSelections();
27126         var el = this.dataName  ?
27127             this.el.child('.roo-tpl-' + this.dataName) :
27128             this.el; 
27129         
27130         el.dom.removeChild(this.nodes[index]);
27131         this.updateIndexes(index);
27132     },
27133
27134     /**
27135      * Refresh an individual node.
27136      * @param {Number} index
27137      */
27138     refreshNode : function(index){
27139         this.onUpdate(this.store, this.store.getAt(index));
27140     },
27141
27142     updateIndexes : function(startIndex, endIndex){
27143         var ns = this.nodes;
27144         startIndex = startIndex || 0;
27145         endIndex = endIndex || ns.length - 1;
27146         for(var i = startIndex; i <= endIndex; i++){
27147             ns[i].nodeIndex = i;
27148         }
27149     },
27150
27151     /**
27152      * Changes the data store this view uses and refresh the view.
27153      * @param {Store} store
27154      */
27155     setStore : function(store, initial){
27156         if(!initial && this.store){
27157             this.store.un("datachanged", this.refresh);
27158             this.store.un("add", this.onAdd);
27159             this.store.un("remove", this.onRemove);
27160             this.store.un("update", this.onUpdate);
27161             this.store.un("clear", this.refresh);
27162             this.store.un("beforeload", this.onBeforeLoad);
27163             this.store.un("load", this.onLoad);
27164             this.store.un("loadexception", this.onLoad);
27165         }
27166         if(store){
27167           
27168             store.on("datachanged", this.refresh, this);
27169             store.on("add", this.onAdd, this);
27170             store.on("remove", this.onRemove, this);
27171             store.on("update", this.onUpdate, this);
27172             store.on("clear", this.refresh, this);
27173             store.on("beforeload", this.onBeforeLoad, this);
27174             store.on("load", this.onLoad, this);
27175             store.on("loadexception", this.onLoad, this);
27176         }
27177         
27178         if(store){
27179             this.refresh();
27180         }
27181     },
27182     /**
27183      * onbeforeLoad - masks the loading area.
27184      *
27185      */
27186     onBeforeLoad : function(store,opts)
27187     {
27188          //Roo.log('onBeforeLoad');   
27189         if (!opts.add) {
27190             this.el.update("");
27191         }
27192         this.el.mask(this.mask ? this.mask : "Loading" ); 
27193     },
27194     onLoad : function ()
27195     {
27196         this.el.unmask();
27197     },
27198     
27199
27200     /**
27201      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27202      * @param {HTMLElement} node
27203      * @return {HTMLElement} The template node
27204      */
27205     findItemFromChild : function(node){
27206         var el = this.dataName  ?
27207             this.el.child('.roo-tpl-' + this.dataName,true) :
27208             this.el.dom; 
27209         
27210         if(!node || node.parentNode == el){
27211                     return node;
27212             }
27213             var p = node.parentNode;
27214             while(p && p != el){
27215             if(p.parentNode == el){
27216                 return p;
27217             }
27218             p = p.parentNode;
27219         }
27220             return null;
27221     },
27222
27223     /** @ignore */
27224     onClick : function(e){
27225         var item = this.findItemFromChild(e.getTarget());
27226         if(item){
27227             var index = this.indexOf(item);
27228             if(this.onItemClick(item, index, e) !== false){
27229                 this.fireEvent("click", this, index, item, e);
27230             }
27231         }else{
27232             this.clearSelections();
27233         }
27234     },
27235
27236     /** @ignore */
27237     onContextMenu : function(e){
27238         var item = this.findItemFromChild(e.getTarget());
27239         if(item){
27240             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27241         }
27242     },
27243
27244     /** @ignore */
27245     onDblClick : function(e){
27246         var item = this.findItemFromChild(e.getTarget());
27247         if(item){
27248             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27249         }
27250     },
27251
27252     onItemClick : function(item, index, e)
27253     {
27254         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27255             return false;
27256         }
27257         if (this.toggleSelect) {
27258             var m = this.isSelected(item) ? 'unselect' : 'select';
27259             //Roo.log(m);
27260             var _t = this;
27261             _t[m](item, true, false);
27262             return true;
27263         }
27264         if(this.multiSelect || this.singleSelect){
27265             if(this.multiSelect && e.shiftKey && this.lastSelection){
27266                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27267             }else{
27268                 this.select(item, this.multiSelect && e.ctrlKey);
27269                 this.lastSelection = item;
27270             }
27271             
27272             if(!this.tickable){
27273                 e.preventDefault();
27274             }
27275             
27276         }
27277         return true;
27278     },
27279
27280     /**
27281      * Get the number of selected nodes.
27282      * @return {Number}
27283      */
27284     getSelectionCount : function(){
27285         return this.selections.length;
27286     },
27287
27288     /**
27289      * Get the currently selected nodes.
27290      * @return {Array} An array of HTMLElements
27291      */
27292     getSelectedNodes : function(){
27293         return this.selections;
27294     },
27295
27296     /**
27297      * Get the indexes of the selected nodes.
27298      * @return {Array}
27299      */
27300     getSelectedIndexes : function(){
27301         var indexes = [], s = this.selections;
27302         for(var i = 0, len = s.length; i < len; i++){
27303             indexes.push(s[i].nodeIndex);
27304         }
27305         return indexes;
27306     },
27307
27308     /**
27309      * Clear all selections
27310      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27311      */
27312     clearSelections : function(suppressEvent){
27313         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27314             this.cmp.elements = this.selections;
27315             this.cmp.removeClass(this.selectedClass);
27316             this.selections = [];
27317             if(!suppressEvent){
27318                 this.fireEvent("selectionchange", this, this.selections);
27319             }
27320         }
27321     },
27322
27323     /**
27324      * Returns true if the passed node is selected
27325      * @param {HTMLElement/Number} node The node or node index
27326      * @return {Boolean}
27327      */
27328     isSelected : function(node){
27329         var s = this.selections;
27330         if(s.length < 1){
27331             return false;
27332         }
27333         node = this.getNode(node);
27334         return s.indexOf(node) !== -1;
27335     },
27336
27337     /**
27338      * Selects nodes.
27339      * @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
27340      * @param {Boolean} keepExisting (optional) true to keep existing selections
27341      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27342      */
27343     select : function(nodeInfo, keepExisting, suppressEvent){
27344         if(nodeInfo instanceof Array){
27345             if(!keepExisting){
27346                 this.clearSelections(true);
27347             }
27348             for(var i = 0, len = nodeInfo.length; i < len; i++){
27349                 this.select(nodeInfo[i], true, true);
27350             }
27351             return;
27352         } 
27353         var node = this.getNode(nodeInfo);
27354         if(!node || this.isSelected(node)){
27355             return; // already selected.
27356         }
27357         if(!keepExisting){
27358             this.clearSelections(true);
27359         }
27360         
27361         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27362             Roo.fly(node).addClass(this.selectedClass);
27363             this.selections.push(node);
27364             if(!suppressEvent){
27365                 this.fireEvent("selectionchange", this, this.selections);
27366             }
27367         }
27368         
27369         
27370     },
27371       /**
27372      * Unselects nodes.
27373      * @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
27374      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27375      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27376      */
27377     unselect : function(nodeInfo, keepExisting, suppressEvent)
27378     {
27379         if(nodeInfo instanceof Array){
27380             Roo.each(this.selections, function(s) {
27381                 this.unselect(s, nodeInfo);
27382             }, this);
27383             return;
27384         }
27385         var node = this.getNode(nodeInfo);
27386         if(!node || !this.isSelected(node)){
27387             //Roo.log("not selected");
27388             return; // not selected.
27389         }
27390         // fireevent???
27391         var ns = [];
27392         Roo.each(this.selections, function(s) {
27393             if (s == node ) {
27394                 Roo.fly(node).removeClass(this.selectedClass);
27395
27396                 return;
27397             }
27398             ns.push(s);
27399         },this);
27400         
27401         this.selections= ns;
27402         this.fireEvent("selectionchange", this, this.selections);
27403     },
27404
27405     /**
27406      * Gets a template node.
27407      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27408      * @return {HTMLElement} The node or null if it wasn't found
27409      */
27410     getNode : function(nodeInfo){
27411         if(typeof nodeInfo == "string"){
27412             return document.getElementById(nodeInfo);
27413         }else if(typeof nodeInfo == "number"){
27414             return this.nodes[nodeInfo];
27415         }
27416         return nodeInfo;
27417     },
27418
27419     /**
27420      * Gets a range template nodes.
27421      * @param {Number} startIndex
27422      * @param {Number} endIndex
27423      * @return {Array} An array of nodes
27424      */
27425     getNodes : function(start, end){
27426         var ns = this.nodes;
27427         start = start || 0;
27428         end = typeof end == "undefined" ? ns.length - 1 : end;
27429         var nodes = [];
27430         if(start <= end){
27431             for(var i = start; i <= end; i++){
27432                 nodes.push(ns[i]);
27433             }
27434         } else{
27435             for(var i = start; i >= end; i--){
27436                 nodes.push(ns[i]);
27437             }
27438         }
27439         return nodes;
27440     },
27441
27442     /**
27443      * Finds the index of the passed node
27444      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27445      * @return {Number} The index of the node or -1
27446      */
27447     indexOf : function(node){
27448         node = this.getNode(node);
27449         if(typeof node.nodeIndex == "number"){
27450             return node.nodeIndex;
27451         }
27452         var ns = this.nodes;
27453         for(var i = 0, len = ns.length; i < len; i++){
27454             if(ns[i] == node){
27455                 return i;
27456             }
27457         }
27458         return -1;
27459     }
27460 });
27461 /*
27462  * Based on:
27463  * Ext JS Library 1.1.1
27464  * Copyright(c) 2006-2007, Ext JS, LLC.
27465  *
27466  * Originally Released Under LGPL - original licence link has changed is not relivant.
27467  *
27468  * Fork - LGPL
27469  * <script type="text/javascript">
27470  */
27471
27472 /**
27473  * @class Roo.JsonView
27474  * @extends Roo.View
27475  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27476 <pre><code>
27477 var view = new Roo.JsonView({
27478     container: "my-element",
27479     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27480     multiSelect: true, 
27481     jsonRoot: "data" 
27482 });
27483
27484 // listen for node click?
27485 view.on("click", function(vw, index, node, e){
27486     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27487 });
27488
27489 // direct load of JSON data
27490 view.load("foobar.php");
27491
27492 // Example from my blog list
27493 var tpl = new Roo.Template(
27494     '&lt;div class="entry"&gt;' +
27495     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27496     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27497     "&lt;/div&gt;&lt;hr /&gt;"
27498 );
27499
27500 var moreView = new Roo.JsonView({
27501     container :  "entry-list", 
27502     template : tpl,
27503     jsonRoot: "posts"
27504 });
27505 moreView.on("beforerender", this.sortEntries, this);
27506 moreView.load({
27507     url: "/blog/get-posts.php",
27508     params: "allposts=true",
27509     text: "Loading Blog Entries..."
27510 });
27511 </code></pre>
27512
27513 * Note: old code is supported with arguments : (container, template, config)
27514
27515
27516  * @constructor
27517  * Create a new JsonView
27518  * 
27519  * @param {Object} config The config object
27520  * 
27521  */
27522 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27523     
27524     
27525     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27526
27527     var um = this.el.getUpdateManager();
27528     um.setRenderer(this);
27529     um.on("update", this.onLoad, this);
27530     um.on("failure", this.onLoadException, this);
27531
27532     /**
27533      * @event beforerender
27534      * Fires before rendering of the downloaded JSON data.
27535      * @param {Roo.JsonView} this
27536      * @param {Object} data The JSON data loaded
27537      */
27538     /**
27539      * @event load
27540      * Fires when data is loaded.
27541      * @param {Roo.JsonView} this
27542      * @param {Object} data The JSON data loaded
27543      * @param {Object} response The raw Connect response object
27544      */
27545     /**
27546      * @event loadexception
27547      * Fires when loading fails.
27548      * @param {Roo.JsonView} this
27549      * @param {Object} response The raw Connect response object
27550      */
27551     this.addEvents({
27552         'beforerender' : true,
27553         'load' : true,
27554         'loadexception' : true
27555     });
27556 };
27557 Roo.extend(Roo.JsonView, Roo.View, {
27558     /**
27559      * @type {String} The root property in the loaded JSON object that contains the data
27560      */
27561     jsonRoot : "",
27562
27563     /**
27564      * Refreshes the view.
27565      */
27566     refresh : function(){
27567         this.clearSelections();
27568         this.el.update("");
27569         var html = [];
27570         var o = this.jsonData;
27571         if(o && o.length > 0){
27572             for(var i = 0, len = o.length; i < len; i++){
27573                 var data = this.prepareData(o[i], i, o);
27574                 html[html.length] = this.tpl.apply(data);
27575             }
27576         }else{
27577             html.push(this.emptyText);
27578         }
27579         this.el.update(html.join(""));
27580         this.nodes = this.el.dom.childNodes;
27581         this.updateIndexes(0);
27582     },
27583
27584     /**
27585      * 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.
27586      * @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:
27587      <pre><code>
27588      view.load({
27589          url: "your-url.php",
27590          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27591          callback: yourFunction,
27592          scope: yourObject, //(optional scope)
27593          discardUrl: false,
27594          nocache: false,
27595          text: "Loading...",
27596          timeout: 30,
27597          scripts: false
27598      });
27599      </code></pre>
27600      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27601      * 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.
27602      * @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}
27603      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27604      * @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.
27605      */
27606     load : function(){
27607         var um = this.el.getUpdateManager();
27608         um.update.apply(um, arguments);
27609     },
27610
27611     // note - render is a standard framework call...
27612     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27613     render : function(el, response){
27614         
27615         this.clearSelections();
27616         this.el.update("");
27617         var o;
27618         try{
27619             if (response != '') {
27620                 o = Roo.util.JSON.decode(response.responseText);
27621                 if(this.jsonRoot){
27622                     
27623                     o = o[this.jsonRoot];
27624                 }
27625             }
27626         } catch(e){
27627         }
27628         /**
27629          * The current JSON data or null
27630          */
27631         this.jsonData = o;
27632         this.beforeRender();
27633         this.refresh();
27634     },
27635
27636 /**
27637  * Get the number of records in the current JSON dataset
27638  * @return {Number}
27639  */
27640     getCount : function(){
27641         return this.jsonData ? this.jsonData.length : 0;
27642     },
27643
27644 /**
27645  * Returns the JSON object for the specified node(s)
27646  * @param {HTMLElement/Array} node The node or an array of nodes
27647  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27648  * you get the JSON object for the node
27649  */
27650     getNodeData : function(node){
27651         if(node instanceof Array){
27652             var data = [];
27653             for(var i = 0, len = node.length; i < len; i++){
27654                 data.push(this.getNodeData(node[i]));
27655             }
27656             return data;
27657         }
27658         return this.jsonData[this.indexOf(node)] || null;
27659     },
27660
27661     beforeRender : function(){
27662         this.snapshot = this.jsonData;
27663         if(this.sortInfo){
27664             this.sort.apply(this, this.sortInfo);
27665         }
27666         this.fireEvent("beforerender", this, this.jsonData);
27667     },
27668
27669     onLoad : function(el, o){
27670         this.fireEvent("load", this, this.jsonData, o);
27671     },
27672
27673     onLoadException : function(el, o){
27674         this.fireEvent("loadexception", this, o);
27675     },
27676
27677 /**
27678  * Filter the data by a specific property.
27679  * @param {String} property A property on your JSON objects
27680  * @param {String/RegExp} value Either string that the property values
27681  * should start with, or a RegExp to test against the property
27682  */
27683     filter : function(property, value){
27684         if(this.jsonData){
27685             var data = [];
27686             var ss = this.snapshot;
27687             if(typeof value == "string"){
27688                 var vlen = value.length;
27689                 if(vlen == 0){
27690                     this.clearFilter();
27691                     return;
27692                 }
27693                 value = value.toLowerCase();
27694                 for(var i = 0, len = ss.length; i < len; i++){
27695                     var o = ss[i];
27696                     if(o[property].substr(0, vlen).toLowerCase() == value){
27697                         data.push(o);
27698                     }
27699                 }
27700             } else if(value.exec){ // regex?
27701                 for(var i = 0, len = ss.length; i < len; i++){
27702                     var o = ss[i];
27703                     if(value.test(o[property])){
27704                         data.push(o);
27705                     }
27706                 }
27707             } else{
27708                 return;
27709             }
27710             this.jsonData = data;
27711             this.refresh();
27712         }
27713     },
27714
27715 /**
27716  * Filter by a function. The passed function will be called with each
27717  * object in the current dataset. If the function returns true the value is kept,
27718  * otherwise it is filtered.
27719  * @param {Function} fn
27720  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27721  */
27722     filterBy : function(fn, scope){
27723         if(this.jsonData){
27724             var data = [];
27725             var ss = this.snapshot;
27726             for(var i = 0, len = ss.length; i < len; i++){
27727                 var o = ss[i];
27728                 if(fn.call(scope || this, o)){
27729                     data.push(o);
27730                 }
27731             }
27732             this.jsonData = data;
27733             this.refresh();
27734         }
27735     },
27736
27737 /**
27738  * Clears the current filter.
27739  */
27740     clearFilter : function(){
27741         if(this.snapshot && this.jsonData != this.snapshot){
27742             this.jsonData = this.snapshot;
27743             this.refresh();
27744         }
27745     },
27746
27747
27748 /**
27749  * Sorts the data for this view and refreshes it.
27750  * @param {String} property A property on your JSON objects to sort on
27751  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27752  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27753  */
27754     sort : function(property, dir, sortType){
27755         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27756         if(this.jsonData){
27757             var p = property;
27758             var dsc = dir && dir.toLowerCase() == "desc";
27759             var f = function(o1, o2){
27760                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27761                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27762                 ;
27763                 if(v1 < v2){
27764                     return dsc ? +1 : -1;
27765                 } else if(v1 > v2){
27766                     return dsc ? -1 : +1;
27767                 } else{
27768                     return 0;
27769                 }
27770             };
27771             this.jsonData.sort(f);
27772             this.refresh();
27773             if(this.jsonData != this.snapshot){
27774                 this.snapshot.sort(f);
27775             }
27776         }
27777     }
27778 });/*
27779  * Based on:
27780  * Ext JS Library 1.1.1
27781  * Copyright(c) 2006-2007, Ext JS, LLC.
27782  *
27783  * Originally Released Under LGPL - original licence link has changed is not relivant.
27784  *
27785  * Fork - LGPL
27786  * <script type="text/javascript">
27787  */
27788  
27789
27790 /**
27791  * @class Roo.ColorPalette
27792  * @extends Roo.Component
27793  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27794  * Here's an example of typical usage:
27795  * <pre><code>
27796 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27797 cp.render('my-div');
27798
27799 cp.on('select', function(palette, selColor){
27800     // do something with selColor
27801 });
27802 </code></pre>
27803  * @constructor
27804  * Create a new ColorPalette
27805  * @param {Object} config The config object
27806  */
27807 Roo.ColorPalette = function(config){
27808     Roo.ColorPalette.superclass.constructor.call(this, config);
27809     this.addEvents({
27810         /**
27811              * @event select
27812              * Fires when a color is selected
27813              * @param {ColorPalette} this
27814              * @param {String} color The 6-digit color hex code (without the # symbol)
27815              */
27816         select: true
27817     });
27818
27819     if(this.handler){
27820         this.on("select", this.handler, this.scope, true);
27821     }
27822 };
27823 Roo.extend(Roo.ColorPalette, Roo.Component, {
27824     /**
27825      * @cfg {String} itemCls
27826      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27827      */
27828     itemCls : "x-color-palette",
27829     /**
27830      * @cfg {String} value
27831      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27832      * the hex codes are case-sensitive.
27833      */
27834     value : null,
27835     clickEvent:'click',
27836     // private
27837     ctype: "Roo.ColorPalette",
27838
27839     /**
27840      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27841      */
27842     allowReselect : false,
27843
27844     /**
27845      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27846      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27847      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27848      * of colors with the width setting until the box is symmetrical.</p>
27849      * <p>You can override individual colors if needed:</p>
27850      * <pre><code>
27851 var cp = new Roo.ColorPalette();
27852 cp.colors[0] = "FF0000";  // change the first box to red
27853 </code></pre>
27854
27855 Or you can provide a custom array of your own for complete control:
27856 <pre><code>
27857 var cp = new Roo.ColorPalette();
27858 cp.colors = ["000000", "993300", "333300"];
27859 </code></pre>
27860      * @type Array
27861      */
27862     colors : [
27863         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27864         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27865         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27866         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27867         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27868     ],
27869
27870     // private
27871     onRender : function(container, position){
27872         var t = new Roo.MasterTemplate(
27873             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27874         );
27875         var c = this.colors;
27876         for(var i = 0, len = c.length; i < len; i++){
27877             t.add([c[i]]);
27878         }
27879         var el = document.createElement("div");
27880         el.className = this.itemCls;
27881         t.overwrite(el);
27882         container.dom.insertBefore(el, position);
27883         this.el = Roo.get(el);
27884         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27885         if(this.clickEvent != 'click'){
27886             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27887         }
27888     },
27889
27890     // private
27891     afterRender : function(){
27892         Roo.ColorPalette.superclass.afterRender.call(this);
27893         if(this.value){
27894             var s = this.value;
27895             this.value = null;
27896             this.select(s);
27897         }
27898     },
27899
27900     // private
27901     handleClick : function(e, t){
27902         e.preventDefault();
27903         if(!this.disabled){
27904             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27905             this.select(c.toUpperCase());
27906         }
27907     },
27908
27909     /**
27910      * Selects the specified color in the palette (fires the select event)
27911      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27912      */
27913     select : function(color){
27914         color = color.replace("#", "");
27915         if(color != this.value || this.allowReselect){
27916             var el = this.el;
27917             if(this.value){
27918                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27919             }
27920             el.child("a.color-"+color).addClass("x-color-palette-sel");
27921             this.value = color;
27922             this.fireEvent("select", this, color);
27923         }
27924     }
27925 });/*
27926  * Based on:
27927  * Ext JS Library 1.1.1
27928  * Copyright(c) 2006-2007, Ext JS, LLC.
27929  *
27930  * Originally Released Under LGPL - original licence link has changed is not relivant.
27931  *
27932  * Fork - LGPL
27933  * <script type="text/javascript">
27934  */
27935  
27936 /**
27937  * @class Roo.DatePicker
27938  * @extends Roo.Component
27939  * Simple date picker class.
27940  * @constructor
27941  * Create a new DatePicker
27942  * @param {Object} config The config object
27943  */
27944 Roo.DatePicker = function(config){
27945     Roo.DatePicker.superclass.constructor.call(this, config);
27946
27947     this.value = config && config.value ?
27948                  config.value.clearTime() : new Date().clearTime();
27949
27950     this.addEvents({
27951         /**
27952              * @event select
27953              * Fires when a date is selected
27954              * @param {DatePicker} this
27955              * @param {Date} date The selected date
27956              */
27957         'select': true,
27958         /**
27959              * @event monthchange
27960              * Fires when the displayed month changes 
27961              * @param {DatePicker} this
27962              * @param {Date} date The selected month
27963              */
27964         'monthchange': true
27965     });
27966
27967     if(this.handler){
27968         this.on("select", this.handler,  this.scope || this);
27969     }
27970     // build the disabledDatesRE
27971     if(!this.disabledDatesRE && this.disabledDates){
27972         var dd = this.disabledDates;
27973         var re = "(?:";
27974         for(var i = 0; i < dd.length; i++){
27975             re += dd[i];
27976             if(i != dd.length-1) {
27977                 re += "|";
27978             }
27979         }
27980         this.disabledDatesRE = new RegExp(re + ")");
27981     }
27982 };
27983
27984 Roo.extend(Roo.DatePicker, Roo.Component, {
27985     /**
27986      * @cfg {String} todayText
27987      * The text to display on the button that selects the current date (defaults to "Today")
27988      */
27989     todayText : "Today",
27990     /**
27991      * @cfg {String} okText
27992      * The text to display on the ok button
27993      */
27994     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27995     /**
27996      * @cfg {String} cancelText
27997      * The text to display on the cancel button
27998      */
27999     cancelText : "Cancel",
28000     /**
28001      * @cfg {String} todayTip
28002      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28003      */
28004     todayTip : "{0} (Spacebar)",
28005     /**
28006      * @cfg {Date} minDate
28007      * Minimum allowable date (JavaScript date object, defaults to null)
28008      */
28009     minDate : null,
28010     /**
28011      * @cfg {Date} maxDate
28012      * Maximum allowable date (JavaScript date object, defaults to null)
28013      */
28014     maxDate : null,
28015     /**
28016      * @cfg {String} minText
28017      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28018      */
28019     minText : "This date is before the minimum date",
28020     /**
28021      * @cfg {String} maxText
28022      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28023      */
28024     maxText : "This date is after the maximum date",
28025     /**
28026      * @cfg {String} format
28027      * The default date format string which can be overriden for localization support.  The format must be
28028      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28029      */
28030     format : "m/d/y",
28031     /**
28032      * @cfg {Array} disabledDays
28033      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28034      */
28035     disabledDays : null,
28036     /**
28037      * @cfg {String} disabledDaysText
28038      * The tooltip to display when the date falls on a disabled day (defaults to "")
28039      */
28040     disabledDaysText : "",
28041     /**
28042      * @cfg {RegExp} disabledDatesRE
28043      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28044      */
28045     disabledDatesRE : null,
28046     /**
28047      * @cfg {String} disabledDatesText
28048      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28049      */
28050     disabledDatesText : "",
28051     /**
28052      * @cfg {Boolean} constrainToViewport
28053      * True to constrain the date picker to the viewport (defaults to true)
28054      */
28055     constrainToViewport : true,
28056     /**
28057      * @cfg {Array} monthNames
28058      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28059      */
28060     monthNames : Date.monthNames,
28061     /**
28062      * @cfg {Array} dayNames
28063      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28064      */
28065     dayNames : Date.dayNames,
28066     /**
28067      * @cfg {String} nextText
28068      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28069      */
28070     nextText: 'Next Month (Control+Right)',
28071     /**
28072      * @cfg {String} prevText
28073      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28074      */
28075     prevText: 'Previous Month (Control+Left)',
28076     /**
28077      * @cfg {String} monthYearText
28078      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28079      */
28080     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28081     /**
28082      * @cfg {Number} startDay
28083      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28084      */
28085     startDay : 0,
28086     /**
28087      * @cfg {Bool} showClear
28088      * Show a clear button (usefull for date form elements that can be blank.)
28089      */
28090     
28091     showClear: false,
28092     
28093     /**
28094      * Sets the value of the date field
28095      * @param {Date} value The date to set
28096      */
28097     setValue : function(value){
28098         var old = this.value;
28099         
28100         if (typeof(value) == 'string') {
28101          
28102             value = Date.parseDate(value, this.format);
28103         }
28104         if (!value) {
28105             value = new Date();
28106         }
28107         
28108         this.value = value.clearTime(true);
28109         if(this.el){
28110             this.update(this.value);
28111         }
28112     },
28113
28114     /**
28115      * Gets the current selected value of the date field
28116      * @return {Date} The selected date
28117      */
28118     getValue : function(){
28119         return this.value;
28120     },
28121
28122     // private
28123     focus : function(){
28124         if(this.el){
28125             this.update(this.activeDate);
28126         }
28127     },
28128
28129     // privateval
28130     onRender : function(container, position){
28131         
28132         var m = [
28133              '<table cellspacing="0">',
28134                 '<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>',
28135                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28136         var dn = this.dayNames;
28137         for(var i = 0; i < 7; i++){
28138             var d = this.startDay+i;
28139             if(d > 6){
28140                 d = d-7;
28141             }
28142             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28143         }
28144         m[m.length] = "</tr></thead><tbody><tr>";
28145         for(var i = 0; i < 42; i++) {
28146             if(i % 7 == 0 && i != 0){
28147                 m[m.length] = "</tr><tr>";
28148             }
28149             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28150         }
28151         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28152             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28153
28154         var el = document.createElement("div");
28155         el.className = "x-date-picker";
28156         el.innerHTML = m.join("");
28157
28158         container.dom.insertBefore(el, position);
28159
28160         this.el = Roo.get(el);
28161         this.eventEl = Roo.get(el.firstChild);
28162
28163         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28164             handler: this.showPrevMonth,
28165             scope: this,
28166             preventDefault:true,
28167             stopDefault:true
28168         });
28169
28170         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28171             handler: this.showNextMonth,
28172             scope: this,
28173             preventDefault:true,
28174             stopDefault:true
28175         });
28176
28177         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28178
28179         this.monthPicker = this.el.down('div.x-date-mp');
28180         this.monthPicker.enableDisplayMode('block');
28181         
28182         var kn = new Roo.KeyNav(this.eventEl, {
28183             "left" : function(e){
28184                 e.ctrlKey ?
28185                     this.showPrevMonth() :
28186                     this.update(this.activeDate.add("d", -1));
28187             },
28188
28189             "right" : function(e){
28190                 e.ctrlKey ?
28191                     this.showNextMonth() :
28192                     this.update(this.activeDate.add("d", 1));
28193             },
28194
28195             "up" : function(e){
28196                 e.ctrlKey ?
28197                     this.showNextYear() :
28198                     this.update(this.activeDate.add("d", -7));
28199             },
28200
28201             "down" : function(e){
28202                 e.ctrlKey ?
28203                     this.showPrevYear() :
28204                     this.update(this.activeDate.add("d", 7));
28205             },
28206
28207             "pageUp" : function(e){
28208                 this.showNextMonth();
28209             },
28210
28211             "pageDown" : function(e){
28212                 this.showPrevMonth();
28213             },
28214
28215             "enter" : function(e){
28216                 e.stopPropagation();
28217                 return true;
28218             },
28219
28220             scope : this
28221         });
28222
28223         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28224
28225         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28226
28227         this.el.unselectable();
28228         
28229         this.cells = this.el.select("table.x-date-inner tbody td");
28230         this.textNodes = this.el.query("table.x-date-inner tbody span");
28231
28232         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28233             text: "&#160;",
28234             tooltip: this.monthYearText
28235         });
28236
28237         this.mbtn.on('click', this.showMonthPicker, this);
28238         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28239
28240
28241         var today = (new Date()).dateFormat(this.format);
28242         
28243         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28244         if (this.showClear) {
28245             baseTb.add( new Roo.Toolbar.Fill());
28246         }
28247         baseTb.add({
28248             text: String.format(this.todayText, today),
28249             tooltip: String.format(this.todayTip, today),
28250             handler: this.selectToday,
28251             scope: this
28252         });
28253         
28254         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28255             
28256         //});
28257         if (this.showClear) {
28258             
28259             baseTb.add( new Roo.Toolbar.Fill());
28260             baseTb.add({
28261                 text: '&#160;',
28262                 cls: 'x-btn-icon x-btn-clear',
28263                 handler: function() {
28264                     //this.value = '';
28265                     this.fireEvent("select", this, '');
28266                 },
28267                 scope: this
28268             });
28269         }
28270         
28271         
28272         if(Roo.isIE){
28273             this.el.repaint();
28274         }
28275         this.update(this.value);
28276     },
28277
28278     createMonthPicker : function(){
28279         if(!this.monthPicker.dom.firstChild){
28280             var buf = ['<table border="0" cellspacing="0">'];
28281             for(var i = 0; i < 6; i++){
28282                 buf.push(
28283                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28284                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28285                     i == 0 ?
28286                     '<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>' :
28287                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28288                 );
28289             }
28290             buf.push(
28291                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28292                     this.okText,
28293                     '</button><button type="button" class="x-date-mp-cancel">',
28294                     this.cancelText,
28295                     '</button></td></tr>',
28296                 '</table>'
28297             );
28298             this.monthPicker.update(buf.join(''));
28299             this.monthPicker.on('click', this.onMonthClick, this);
28300             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28301
28302             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28303             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28304
28305             this.mpMonths.each(function(m, a, i){
28306                 i += 1;
28307                 if((i%2) == 0){
28308                     m.dom.xmonth = 5 + Math.round(i * .5);
28309                 }else{
28310                     m.dom.xmonth = Math.round((i-1) * .5);
28311                 }
28312             });
28313         }
28314     },
28315
28316     showMonthPicker : function(){
28317         this.createMonthPicker();
28318         var size = this.el.getSize();
28319         this.monthPicker.setSize(size);
28320         this.monthPicker.child('table').setSize(size);
28321
28322         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28323         this.updateMPMonth(this.mpSelMonth);
28324         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28325         this.updateMPYear(this.mpSelYear);
28326
28327         this.monthPicker.slideIn('t', {duration:.2});
28328     },
28329
28330     updateMPYear : function(y){
28331         this.mpyear = y;
28332         var ys = this.mpYears.elements;
28333         for(var i = 1; i <= 10; i++){
28334             var td = ys[i-1], y2;
28335             if((i%2) == 0){
28336                 y2 = y + Math.round(i * .5);
28337                 td.firstChild.innerHTML = y2;
28338                 td.xyear = y2;
28339             }else{
28340                 y2 = y - (5-Math.round(i * .5));
28341                 td.firstChild.innerHTML = y2;
28342                 td.xyear = y2;
28343             }
28344             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28345         }
28346     },
28347
28348     updateMPMonth : function(sm){
28349         this.mpMonths.each(function(m, a, i){
28350             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28351         });
28352     },
28353
28354     selectMPMonth: function(m){
28355         
28356     },
28357
28358     onMonthClick : function(e, t){
28359         e.stopEvent();
28360         var el = new Roo.Element(t), pn;
28361         if(el.is('button.x-date-mp-cancel')){
28362             this.hideMonthPicker();
28363         }
28364         else if(el.is('button.x-date-mp-ok')){
28365             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28366             this.hideMonthPicker();
28367         }
28368         else if(pn = el.up('td.x-date-mp-month', 2)){
28369             this.mpMonths.removeClass('x-date-mp-sel');
28370             pn.addClass('x-date-mp-sel');
28371             this.mpSelMonth = pn.dom.xmonth;
28372         }
28373         else if(pn = el.up('td.x-date-mp-year', 2)){
28374             this.mpYears.removeClass('x-date-mp-sel');
28375             pn.addClass('x-date-mp-sel');
28376             this.mpSelYear = pn.dom.xyear;
28377         }
28378         else if(el.is('a.x-date-mp-prev')){
28379             this.updateMPYear(this.mpyear-10);
28380         }
28381         else if(el.is('a.x-date-mp-next')){
28382             this.updateMPYear(this.mpyear+10);
28383         }
28384     },
28385
28386     onMonthDblClick : function(e, t){
28387         e.stopEvent();
28388         var el = new Roo.Element(t), pn;
28389         if(pn = el.up('td.x-date-mp-month', 2)){
28390             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28391             this.hideMonthPicker();
28392         }
28393         else if(pn = el.up('td.x-date-mp-year', 2)){
28394             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28395             this.hideMonthPicker();
28396         }
28397     },
28398
28399     hideMonthPicker : function(disableAnim){
28400         if(this.monthPicker){
28401             if(disableAnim === true){
28402                 this.monthPicker.hide();
28403             }else{
28404                 this.monthPicker.slideOut('t', {duration:.2});
28405             }
28406         }
28407     },
28408
28409     // private
28410     showPrevMonth : function(e){
28411         this.update(this.activeDate.add("mo", -1));
28412     },
28413
28414     // private
28415     showNextMonth : function(e){
28416         this.update(this.activeDate.add("mo", 1));
28417     },
28418
28419     // private
28420     showPrevYear : function(){
28421         this.update(this.activeDate.add("y", -1));
28422     },
28423
28424     // private
28425     showNextYear : function(){
28426         this.update(this.activeDate.add("y", 1));
28427     },
28428
28429     // private
28430     handleMouseWheel : function(e){
28431         var delta = e.getWheelDelta();
28432         if(delta > 0){
28433             this.showPrevMonth();
28434             e.stopEvent();
28435         } else if(delta < 0){
28436             this.showNextMonth();
28437             e.stopEvent();
28438         }
28439     },
28440
28441     // private
28442     handleDateClick : function(e, t){
28443         e.stopEvent();
28444         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28445             this.setValue(new Date(t.dateValue));
28446             this.fireEvent("select", this, this.value);
28447         }
28448     },
28449
28450     // private
28451     selectToday : function(){
28452         this.setValue(new Date().clearTime());
28453         this.fireEvent("select", this, this.value);
28454     },
28455
28456     // private
28457     update : function(date)
28458     {
28459         var vd = this.activeDate;
28460         this.activeDate = date;
28461         if(vd && this.el){
28462             var t = date.getTime();
28463             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28464                 this.cells.removeClass("x-date-selected");
28465                 this.cells.each(function(c){
28466                    if(c.dom.firstChild.dateValue == t){
28467                        c.addClass("x-date-selected");
28468                        setTimeout(function(){
28469                             try{c.dom.firstChild.focus();}catch(e){}
28470                        }, 50);
28471                        return false;
28472                    }
28473                 });
28474                 return;
28475             }
28476         }
28477         
28478         var days = date.getDaysInMonth();
28479         var firstOfMonth = date.getFirstDateOfMonth();
28480         var startingPos = firstOfMonth.getDay()-this.startDay;
28481
28482         if(startingPos <= this.startDay){
28483             startingPos += 7;
28484         }
28485
28486         var pm = date.add("mo", -1);
28487         var prevStart = pm.getDaysInMonth()-startingPos;
28488
28489         var cells = this.cells.elements;
28490         var textEls = this.textNodes;
28491         days += startingPos;
28492
28493         // convert everything to numbers so it's fast
28494         var day = 86400000;
28495         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28496         var today = new Date().clearTime().getTime();
28497         var sel = date.clearTime().getTime();
28498         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28499         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28500         var ddMatch = this.disabledDatesRE;
28501         var ddText = this.disabledDatesText;
28502         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28503         var ddaysText = this.disabledDaysText;
28504         var format = this.format;
28505
28506         var setCellClass = function(cal, cell){
28507             cell.title = "";
28508             var t = d.getTime();
28509             cell.firstChild.dateValue = t;
28510             if(t == today){
28511                 cell.className += " x-date-today";
28512                 cell.title = cal.todayText;
28513             }
28514             if(t == sel){
28515                 cell.className += " x-date-selected";
28516                 setTimeout(function(){
28517                     try{cell.firstChild.focus();}catch(e){}
28518                 }, 50);
28519             }
28520             // disabling
28521             if(t < min) {
28522                 cell.className = " x-date-disabled";
28523                 cell.title = cal.minText;
28524                 return;
28525             }
28526             if(t > max) {
28527                 cell.className = " x-date-disabled";
28528                 cell.title = cal.maxText;
28529                 return;
28530             }
28531             if(ddays){
28532                 if(ddays.indexOf(d.getDay()) != -1){
28533                     cell.title = ddaysText;
28534                     cell.className = " x-date-disabled";
28535                 }
28536             }
28537             if(ddMatch && format){
28538                 var fvalue = d.dateFormat(format);
28539                 if(ddMatch.test(fvalue)){
28540                     cell.title = ddText.replace("%0", fvalue);
28541                     cell.className = " x-date-disabled";
28542                 }
28543             }
28544         };
28545
28546         var i = 0;
28547         for(; i < startingPos; i++) {
28548             textEls[i].innerHTML = (++prevStart);
28549             d.setDate(d.getDate()+1);
28550             cells[i].className = "x-date-prevday";
28551             setCellClass(this, cells[i]);
28552         }
28553         for(; i < days; i++){
28554             intDay = i - startingPos + 1;
28555             textEls[i].innerHTML = (intDay);
28556             d.setDate(d.getDate()+1);
28557             cells[i].className = "x-date-active";
28558             setCellClass(this, cells[i]);
28559         }
28560         var extraDays = 0;
28561         for(; i < 42; i++) {
28562              textEls[i].innerHTML = (++extraDays);
28563              d.setDate(d.getDate()+1);
28564              cells[i].className = "x-date-nextday";
28565              setCellClass(this, cells[i]);
28566         }
28567
28568         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28569         this.fireEvent('monthchange', this, date);
28570         
28571         if(!this.internalRender){
28572             var main = this.el.dom.firstChild;
28573             var w = main.offsetWidth;
28574             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28575             Roo.fly(main).setWidth(w);
28576             this.internalRender = true;
28577             // opera does not respect the auto grow header center column
28578             // then, after it gets a width opera refuses to recalculate
28579             // without a second pass
28580             if(Roo.isOpera && !this.secondPass){
28581                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28582                 this.secondPass = true;
28583                 this.update.defer(10, this, [date]);
28584             }
28585         }
28586         
28587         
28588     }
28589 });        /*
28590  * Based on:
28591  * Ext JS Library 1.1.1
28592  * Copyright(c) 2006-2007, Ext JS, LLC.
28593  *
28594  * Originally Released Under LGPL - original licence link has changed is not relivant.
28595  *
28596  * Fork - LGPL
28597  * <script type="text/javascript">
28598  */
28599 /**
28600  * @class Roo.TabPanel
28601  * @extends Roo.util.Observable
28602  * A lightweight tab container.
28603  * <br><br>
28604  * Usage:
28605  * <pre><code>
28606 // basic tabs 1, built from existing content
28607 var tabs = new Roo.TabPanel("tabs1");
28608 tabs.addTab("script", "View Script");
28609 tabs.addTab("markup", "View Markup");
28610 tabs.activate("script");
28611
28612 // more advanced tabs, built from javascript
28613 var jtabs = new Roo.TabPanel("jtabs");
28614 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28615
28616 // set up the UpdateManager
28617 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28618 var updater = tab2.getUpdateManager();
28619 updater.setDefaultUrl("ajax1.htm");
28620 tab2.on('activate', updater.refresh, updater, true);
28621
28622 // Use setUrl for Ajax loading
28623 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28624 tab3.setUrl("ajax2.htm", null, true);
28625
28626 // Disabled tab
28627 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28628 tab4.disable();
28629
28630 jtabs.activate("jtabs-1");
28631  * </code></pre>
28632  * @constructor
28633  * Create a new TabPanel.
28634  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28635  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28636  */
28637 Roo.TabPanel = function(container, config){
28638     /**
28639     * The container element for this TabPanel.
28640     * @type Roo.Element
28641     */
28642     this.el = Roo.get(container, true);
28643     if(config){
28644         if(typeof config == "boolean"){
28645             this.tabPosition = config ? "bottom" : "top";
28646         }else{
28647             Roo.apply(this, config);
28648         }
28649     }
28650     if(this.tabPosition == "bottom"){
28651         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28652         this.el.addClass("x-tabs-bottom");
28653     }
28654     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28655     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28656     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28657     if(Roo.isIE){
28658         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28659     }
28660     if(this.tabPosition != "bottom"){
28661         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28662          * @type Roo.Element
28663          */
28664         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28665         this.el.addClass("x-tabs-top");
28666     }
28667     this.items = [];
28668
28669     this.bodyEl.setStyle("position", "relative");
28670
28671     this.active = null;
28672     this.activateDelegate = this.activate.createDelegate(this);
28673
28674     this.addEvents({
28675         /**
28676          * @event tabchange
28677          * Fires when the active tab changes
28678          * @param {Roo.TabPanel} this
28679          * @param {Roo.TabPanelItem} activePanel The new active tab
28680          */
28681         "tabchange": true,
28682         /**
28683          * @event beforetabchange
28684          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28685          * @param {Roo.TabPanel} this
28686          * @param {Object} e Set cancel to true on this object to cancel the tab change
28687          * @param {Roo.TabPanelItem} tab The tab being changed to
28688          */
28689         "beforetabchange" : true
28690     });
28691
28692     Roo.EventManager.onWindowResize(this.onResize, this);
28693     this.cpad = this.el.getPadding("lr");
28694     this.hiddenCount = 0;
28695
28696
28697     // toolbar on the tabbar support...
28698     if (this.toolbar) {
28699         var tcfg = this.toolbar;
28700         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28701         this.toolbar = new Roo.Toolbar(tcfg);
28702         if (Roo.isSafari) {
28703             var tbl = tcfg.container.child('table', true);
28704             tbl.setAttribute('width', '100%');
28705         }
28706         
28707     }
28708    
28709
28710
28711     Roo.TabPanel.superclass.constructor.call(this);
28712 };
28713
28714 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28715     /*
28716      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28717      */
28718     tabPosition : "top",
28719     /*
28720      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28721      */
28722     currentTabWidth : 0,
28723     /*
28724      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28725      */
28726     minTabWidth : 40,
28727     /*
28728      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28729      */
28730     maxTabWidth : 250,
28731     /*
28732      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28733      */
28734     preferredTabWidth : 175,
28735     /*
28736      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28737      */
28738     resizeTabs : false,
28739     /*
28740      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28741      */
28742     monitorResize : true,
28743     /*
28744      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28745      */
28746     toolbar : false,
28747
28748     /**
28749      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28750      * @param {String} id The id of the div to use <b>or create</b>
28751      * @param {String} text The text for the tab
28752      * @param {String} content (optional) Content to put in the TabPanelItem body
28753      * @param {Boolean} closable (optional) True to create a close icon on the tab
28754      * @return {Roo.TabPanelItem} The created TabPanelItem
28755      */
28756     addTab : function(id, text, content, closable){
28757         var item = new Roo.TabPanelItem(this, id, text, closable);
28758         this.addTabItem(item);
28759         if(content){
28760             item.setContent(content);
28761         }
28762         return item;
28763     },
28764
28765     /**
28766      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28767      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28768      * @return {Roo.TabPanelItem}
28769      */
28770     getTab : function(id){
28771         return this.items[id];
28772     },
28773
28774     /**
28775      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28776      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28777      */
28778     hideTab : function(id){
28779         var t = this.items[id];
28780         if(!t.isHidden()){
28781            t.setHidden(true);
28782            this.hiddenCount++;
28783            this.autoSizeTabs();
28784         }
28785     },
28786
28787     /**
28788      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28789      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28790      */
28791     unhideTab : function(id){
28792         var t = this.items[id];
28793         if(t.isHidden()){
28794            t.setHidden(false);
28795            this.hiddenCount--;
28796            this.autoSizeTabs();
28797         }
28798     },
28799
28800     /**
28801      * Adds an existing {@link Roo.TabPanelItem}.
28802      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28803      */
28804     addTabItem : function(item){
28805         this.items[item.id] = item;
28806         this.items.push(item);
28807         if(this.resizeTabs){
28808            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28809            this.autoSizeTabs();
28810         }else{
28811             item.autoSize();
28812         }
28813     },
28814
28815     /**
28816      * Removes a {@link Roo.TabPanelItem}.
28817      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28818      */
28819     removeTab : function(id){
28820         var items = this.items;
28821         var tab = items[id];
28822         if(!tab) { return; }
28823         var index = items.indexOf(tab);
28824         if(this.active == tab && items.length > 1){
28825             var newTab = this.getNextAvailable(index);
28826             if(newTab) {
28827                 newTab.activate();
28828             }
28829         }
28830         this.stripEl.dom.removeChild(tab.pnode.dom);
28831         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28832             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28833         }
28834         items.splice(index, 1);
28835         delete this.items[tab.id];
28836         tab.fireEvent("close", tab);
28837         tab.purgeListeners();
28838         this.autoSizeTabs();
28839     },
28840
28841     getNextAvailable : function(start){
28842         var items = this.items;
28843         var index = start;
28844         // look for a next tab that will slide over to
28845         // replace the one being removed
28846         while(index < items.length){
28847             var item = items[++index];
28848             if(item && !item.isHidden()){
28849                 return item;
28850             }
28851         }
28852         // if one isn't found select the previous tab (on the left)
28853         index = start;
28854         while(index >= 0){
28855             var item = items[--index];
28856             if(item && !item.isHidden()){
28857                 return item;
28858             }
28859         }
28860         return null;
28861     },
28862
28863     /**
28864      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28865      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28866      */
28867     disableTab : function(id){
28868         var tab = this.items[id];
28869         if(tab && this.active != tab){
28870             tab.disable();
28871         }
28872     },
28873
28874     /**
28875      * Enables a {@link Roo.TabPanelItem} that is disabled.
28876      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28877      */
28878     enableTab : function(id){
28879         var tab = this.items[id];
28880         tab.enable();
28881     },
28882
28883     /**
28884      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28885      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28886      * @return {Roo.TabPanelItem} The TabPanelItem.
28887      */
28888     activate : function(id){
28889         var tab = this.items[id];
28890         if(!tab){
28891             return null;
28892         }
28893         if(tab == this.active || tab.disabled){
28894             return tab;
28895         }
28896         var e = {};
28897         this.fireEvent("beforetabchange", this, e, tab);
28898         if(e.cancel !== true && !tab.disabled){
28899             if(this.active){
28900                 this.active.hide();
28901             }
28902             this.active = this.items[id];
28903             this.active.show();
28904             this.fireEvent("tabchange", this, this.active);
28905         }
28906         return tab;
28907     },
28908
28909     /**
28910      * Gets the active {@link Roo.TabPanelItem}.
28911      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28912      */
28913     getActiveTab : function(){
28914         return this.active;
28915     },
28916
28917     /**
28918      * Updates the tab body element to fit the height of the container element
28919      * for overflow scrolling
28920      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28921      */
28922     syncHeight : function(targetHeight){
28923         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28924         var bm = this.bodyEl.getMargins();
28925         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28926         this.bodyEl.setHeight(newHeight);
28927         return newHeight;
28928     },
28929
28930     onResize : function(){
28931         if(this.monitorResize){
28932             this.autoSizeTabs();
28933         }
28934     },
28935
28936     /**
28937      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28938      */
28939     beginUpdate : function(){
28940         this.updating = true;
28941     },
28942
28943     /**
28944      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28945      */
28946     endUpdate : function(){
28947         this.updating = false;
28948         this.autoSizeTabs();
28949     },
28950
28951     /**
28952      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28953      */
28954     autoSizeTabs : function(){
28955         var count = this.items.length;
28956         var vcount = count - this.hiddenCount;
28957         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28958             return;
28959         }
28960         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28961         var availWidth = Math.floor(w / vcount);
28962         var b = this.stripBody;
28963         if(b.getWidth() > w){
28964             var tabs = this.items;
28965             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28966             if(availWidth < this.minTabWidth){
28967                 /*if(!this.sleft){    // incomplete scrolling code
28968                     this.createScrollButtons();
28969                 }
28970                 this.showScroll();
28971                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28972             }
28973         }else{
28974             if(this.currentTabWidth < this.preferredTabWidth){
28975                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28976             }
28977         }
28978     },
28979
28980     /**
28981      * Returns the number of tabs in this TabPanel.
28982      * @return {Number}
28983      */
28984      getCount : function(){
28985          return this.items.length;
28986      },
28987
28988     /**
28989      * Resizes all the tabs to the passed width
28990      * @param {Number} The new width
28991      */
28992     setTabWidth : function(width){
28993         this.currentTabWidth = width;
28994         for(var i = 0, len = this.items.length; i < len; i++) {
28995                 if(!this.items[i].isHidden()) {
28996                 this.items[i].setWidth(width);
28997             }
28998         }
28999     },
29000
29001     /**
29002      * Destroys this TabPanel
29003      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29004      */
29005     destroy : function(removeEl){
29006         Roo.EventManager.removeResizeListener(this.onResize, this);
29007         for(var i = 0, len = this.items.length; i < len; i++){
29008             this.items[i].purgeListeners();
29009         }
29010         if(removeEl === true){
29011             this.el.update("");
29012             this.el.remove();
29013         }
29014     }
29015 });
29016
29017 /**
29018  * @class Roo.TabPanelItem
29019  * @extends Roo.util.Observable
29020  * Represents an individual item (tab plus body) in a TabPanel.
29021  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29022  * @param {String} id The id of this TabPanelItem
29023  * @param {String} text The text for the tab of this TabPanelItem
29024  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29025  */
29026 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29027     /**
29028      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29029      * @type Roo.TabPanel
29030      */
29031     this.tabPanel = tabPanel;
29032     /**
29033      * The id for this TabPanelItem
29034      * @type String
29035      */
29036     this.id = id;
29037     /** @private */
29038     this.disabled = false;
29039     /** @private */
29040     this.text = text;
29041     /** @private */
29042     this.loaded = false;
29043     this.closable = closable;
29044
29045     /**
29046      * The body element for this TabPanelItem.
29047      * @type Roo.Element
29048      */
29049     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29050     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29051     this.bodyEl.setStyle("display", "block");
29052     this.bodyEl.setStyle("zoom", "1");
29053     this.hideAction();
29054
29055     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29056     /** @private */
29057     this.el = Roo.get(els.el, true);
29058     this.inner = Roo.get(els.inner, true);
29059     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29060     this.pnode = Roo.get(els.el.parentNode, true);
29061     this.el.on("mousedown", this.onTabMouseDown, this);
29062     this.el.on("click", this.onTabClick, this);
29063     /** @private */
29064     if(closable){
29065         var c = Roo.get(els.close, true);
29066         c.dom.title = this.closeText;
29067         c.addClassOnOver("close-over");
29068         c.on("click", this.closeClick, this);
29069      }
29070
29071     this.addEvents({
29072          /**
29073          * @event activate
29074          * Fires when this tab becomes the active tab.
29075          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29076          * @param {Roo.TabPanelItem} this
29077          */
29078         "activate": true,
29079         /**
29080          * @event beforeclose
29081          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29082          * @param {Roo.TabPanelItem} this
29083          * @param {Object} e Set cancel to true on this object to cancel the close.
29084          */
29085         "beforeclose": true,
29086         /**
29087          * @event close
29088          * Fires when this tab is closed.
29089          * @param {Roo.TabPanelItem} this
29090          */
29091          "close": true,
29092         /**
29093          * @event deactivate
29094          * Fires when this tab is no longer the active tab.
29095          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29096          * @param {Roo.TabPanelItem} this
29097          */
29098          "deactivate" : true
29099     });
29100     this.hidden = false;
29101
29102     Roo.TabPanelItem.superclass.constructor.call(this);
29103 };
29104
29105 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29106     purgeListeners : function(){
29107        Roo.util.Observable.prototype.purgeListeners.call(this);
29108        this.el.removeAllListeners();
29109     },
29110     /**
29111      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29112      */
29113     show : function(){
29114         this.pnode.addClass("on");
29115         this.showAction();
29116         if(Roo.isOpera){
29117             this.tabPanel.stripWrap.repaint();
29118         }
29119         this.fireEvent("activate", this.tabPanel, this);
29120     },
29121
29122     /**
29123      * Returns true if this tab is the active tab.
29124      * @return {Boolean}
29125      */
29126     isActive : function(){
29127         return this.tabPanel.getActiveTab() == this;
29128     },
29129
29130     /**
29131      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29132      */
29133     hide : function(){
29134         this.pnode.removeClass("on");
29135         this.hideAction();
29136         this.fireEvent("deactivate", this.tabPanel, this);
29137     },
29138
29139     hideAction : function(){
29140         this.bodyEl.hide();
29141         this.bodyEl.setStyle("position", "absolute");
29142         this.bodyEl.setLeft("-20000px");
29143         this.bodyEl.setTop("-20000px");
29144     },
29145
29146     showAction : function(){
29147         this.bodyEl.setStyle("position", "relative");
29148         this.bodyEl.setTop("");
29149         this.bodyEl.setLeft("");
29150         this.bodyEl.show();
29151     },
29152
29153     /**
29154      * Set the tooltip for the tab.
29155      * @param {String} tooltip The tab's tooltip
29156      */
29157     setTooltip : function(text){
29158         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29159             this.textEl.dom.qtip = text;
29160             this.textEl.dom.removeAttribute('title');
29161         }else{
29162             this.textEl.dom.title = text;
29163         }
29164     },
29165
29166     onTabClick : function(e){
29167         e.preventDefault();
29168         this.tabPanel.activate(this.id);
29169     },
29170
29171     onTabMouseDown : function(e){
29172         e.preventDefault();
29173         this.tabPanel.activate(this.id);
29174     },
29175
29176     getWidth : function(){
29177         return this.inner.getWidth();
29178     },
29179
29180     setWidth : function(width){
29181         var iwidth = width - this.pnode.getPadding("lr");
29182         this.inner.setWidth(iwidth);
29183         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29184         this.pnode.setWidth(width);
29185     },
29186
29187     /**
29188      * Show or hide the tab
29189      * @param {Boolean} hidden True to hide or false to show.
29190      */
29191     setHidden : function(hidden){
29192         this.hidden = hidden;
29193         this.pnode.setStyle("display", hidden ? "none" : "");
29194     },
29195
29196     /**
29197      * Returns true if this tab is "hidden"
29198      * @return {Boolean}
29199      */
29200     isHidden : function(){
29201         return this.hidden;
29202     },
29203
29204     /**
29205      * Returns the text for this tab
29206      * @return {String}
29207      */
29208     getText : function(){
29209         return this.text;
29210     },
29211
29212     autoSize : function(){
29213         //this.el.beginMeasure();
29214         this.textEl.setWidth(1);
29215         /*
29216          *  #2804 [new] Tabs in Roojs
29217          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29218          */
29219         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29220         //this.el.endMeasure();
29221     },
29222
29223     /**
29224      * Sets the text for the tab (Note: this also sets the tooltip text)
29225      * @param {String} text The tab's text and tooltip
29226      */
29227     setText : function(text){
29228         this.text = text;
29229         this.textEl.update(text);
29230         this.setTooltip(text);
29231         if(!this.tabPanel.resizeTabs){
29232             this.autoSize();
29233         }
29234     },
29235     /**
29236      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29237      */
29238     activate : function(){
29239         this.tabPanel.activate(this.id);
29240     },
29241
29242     /**
29243      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29244      */
29245     disable : function(){
29246         if(this.tabPanel.active != this){
29247             this.disabled = true;
29248             this.pnode.addClass("disabled");
29249         }
29250     },
29251
29252     /**
29253      * Enables this TabPanelItem if it was previously disabled.
29254      */
29255     enable : function(){
29256         this.disabled = false;
29257         this.pnode.removeClass("disabled");
29258     },
29259
29260     /**
29261      * Sets the content for this TabPanelItem.
29262      * @param {String} content The content
29263      * @param {Boolean} loadScripts true to look for and load scripts
29264      */
29265     setContent : function(content, loadScripts){
29266         this.bodyEl.update(content, loadScripts);
29267     },
29268
29269     /**
29270      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29271      * @return {Roo.UpdateManager} The UpdateManager
29272      */
29273     getUpdateManager : function(){
29274         return this.bodyEl.getUpdateManager();
29275     },
29276
29277     /**
29278      * Set a URL to be used to load the content for this TabPanelItem.
29279      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29280      * @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)
29281      * @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)
29282      * @return {Roo.UpdateManager} The UpdateManager
29283      */
29284     setUrl : function(url, params, loadOnce){
29285         if(this.refreshDelegate){
29286             this.un('activate', this.refreshDelegate);
29287         }
29288         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29289         this.on("activate", this.refreshDelegate);
29290         return this.bodyEl.getUpdateManager();
29291     },
29292
29293     /** @private */
29294     _handleRefresh : function(url, params, loadOnce){
29295         if(!loadOnce || !this.loaded){
29296             var updater = this.bodyEl.getUpdateManager();
29297             updater.update(url, params, this._setLoaded.createDelegate(this));
29298         }
29299     },
29300
29301     /**
29302      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29303      *   Will fail silently if the setUrl method has not been called.
29304      *   This does not activate the panel, just updates its content.
29305      */
29306     refresh : function(){
29307         if(this.refreshDelegate){
29308            this.loaded = false;
29309            this.refreshDelegate();
29310         }
29311     },
29312
29313     /** @private */
29314     _setLoaded : function(){
29315         this.loaded = true;
29316     },
29317
29318     /** @private */
29319     closeClick : function(e){
29320         var o = {};
29321         e.stopEvent();
29322         this.fireEvent("beforeclose", this, o);
29323         if(o.cancel !== true){
29324             this.tabPanel.removeTab(this.id);
29325         }
29326     },
29327     /**
29328      * The text displayed in the tooltip for the close icon.
29329      * @type String
29330      */
29331     closeText : "Close this tab"
29332 });
29333
29334 /** @private */
29335 Roo.TabPanel.prototype.createStrip = function(container){
29336     var strip = document.createElement("div");
29337     strip.className = "x-tabs-wrap";
29338     container.appendChild(strip);
29339     return strip;
29340 };
29341 /** @private */
29342 Roo.TabPanel.prototype.createStripList = function(strip){
29343     // div wrapper for retard IE
29344     // returns the "tr" element.
29345     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29346         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29347         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29348     return strip.firstChild.firstChild.firstChild.firstChild;
29349 };
29350 /** @private */
29351 Roo.TabPanel.prototype.createBody = function(container){
29352     var body = document.createElement("div");
29353     Roo.id(body, "tab-body");
29354     Roo.fly(body).addClass("x-tabs-body");
29355     container.appendChild(body);
29356     return body;
29357 };
29358 /** @private */
29359 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29360     var body = Roo.getDom(id);
29361     if(!body){
29362         body = document.createElement("div");
29363         body.id = id;
29364     }
29365     Roo.fly(body).addClass("x-tabs-item-body");
29366     bodyEl.insertBefore(body, bodyEl.firstChild);
29367     return body;
29368 };
29369 /** @private */
29370 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29371     var td = document.createElement("td");
29372     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29373     //stripEl.appendChild(td);
29374     if(closable){
29375         td.className = "x-tabs-closable";
29376         if(!this.closeTpl){
29377             this.closeTpl = new Roo.Template(
29378                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29379                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29380                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29381             );
29382         }
29383         var el = this.closeTpl.overwrite(td, {"text": text});
29384         var close = el.getElementsByTagName("div")[0];
29385         var inner = el.getElementsByTagName("em")[0];
29386         return {"el": el, "close": close, "inner": inner};
29387     } else {
29388         if(!this.tabTpl){
29389             this.tabTpl = new Roo.Template(
29390                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29391                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29392             );
29393         }
29394         var el = this.tabTpl.overwrite(td, {"text": text});
29395         var inner = el.getElementsByTagName("em")[0];
29396         return {"el": el, "inner": inner};
29397     }
29398 };/*
29399  * Based on:
29400  * Ext JS Library 1.1.1
29401  * Copyright(c) 2006-2007, Ext JS, LLC.
29402  *
29403  * Originally Released Under LGPL - original licence link has changed is not relivant.
29404  *
29405  * Fork - LGPL
29406  * <script type="text/javascript">
29407  */
29408
29409 /**
29410  * @class Roo.Button
29411  * @extends Roo.util.Observable
29412  * Simple Button class
29413  * @cfg {String} text The button text
29414  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29415  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29416  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29417  * @cfg {Object} scope The scope of the handler
29418  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29419  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29420  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29421  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29422  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29423  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29424    applies if enableToggle = true)
29425  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29426  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29427   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29428  * @constructor
29429  * Create a new button
29430  * @param {Object} config The config object
29431  */
29432 Roo.Button = function(renderTo, config)
29433 {
29434     if (!config) {
29435         config = renderTo;
29436         renderTo = config.renderTo || false;
29437     }
29438     
29439     Roo.apply(this, config);
29440     this.addEvents({
29441         /**
29442              * @event click
29443              * Fires when this button is clicked
29444              * @param {Button} this
29445              * @param {EventObject} e The click event
29446              */
29447             "click" : true,
29448         /**
29449              * @event toggle
29450              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29451              * @param {Button} this
29452              * @param {Boolean} pressed
29453              */
29454             "toggle" : true,
29455         /**
29456              * @event mouseover
29457              * Fires when the mouse hovers over the button
29458              * @param {Button} this
29459              * @param {Event} e The event object
29460              */
29461         'mouseover' : true,
29462         /**
29463              * @event mouseout
29464              * Fires when the mouse exits the button
29465              * @param {Button} this
29466              * @param {Event} e The event object
29467              */
29468         'mouseout': true,
29469          /**
29470              * @event render
29471              * Fires when the button is rendered
29472              * @param {Button} this
29473              */
29474         'render': true
29475     });
29476     if(this.menu){
29477         this.menu = Roo.menu.MenuMgr.get(this.menu);
29478     }
29479     // register listeners first!!  - so render can be captured..
29480     Roo.util.Observable.call(this);
29481     if(renderTo){
29482         this.render(renderTo);
29483     }
29484     
29485   
29486 };
29487
29488 Roo.extend(Roo.Button, Roo.util.Observable, {
29489     /**
29490      * 
29491      */
29492     
29493     /**
29494      * Read-only. True if this button is hidden
29495      * @type Boolean
29496      */
29497     hidden : false,
29498     /**
29499      * Read-only. True if this button is disabled
29500      * @type Boolean
29501      */
29502     disabled : false,
29503     /**
29504      * Read-only. True if this button is pressed (only if enableToggle = true)
29505      * @type Boolean
29506      */
29507     pressed : false,
29508
29509     /**
29510      * @cfg {Number} tabIndex 
29511      * The DOM tabIndex for this button (defaults to undefined)
29512      */
29513     tabIndex : undefined,
29514
29515     /**
29516      * @cfg {Boolean} enableToggle
29517      * True to enable pressed/not pressed toggling (defaults to false)
29518      */
29519     enableToggle: false,
29520     /**
29521      * @cfg {Mixed} menu
29522      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29523      */
29524     menu : undefined,
29525     /**
29526      * @cfg {String} menuAlign
29527      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29528      */
29529     menuAlign : "tl-bl?",
29530
29531     /**
29532      * @cfg {String} iconCls
29533      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29534      */
29535     iconCls : undefined,
29536     /**
29537      * @cfg {String} type
29538      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29539      */
29540     type : 'button',
29541
29542     // private
29543     menuClassTarget: 'tr',
29544
29545     /**
29546      * @cfg {String} clickEvent
29547      * The type of event to map to the button's event handler (defaults to 'click')
29548      */
29549     clickEvent : 'click',
29550
29551     /**
29552      * @cfg {Boolean} handleMouseEvents
29553      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29554      */
29555     handleMouseEvents : true,
29556
29557     /**
29558      * @cfg {String} tooltipType
29559      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29560      */
29561     tooltipType : 'qtip',
29562
29563     /**
29564      * @cfg {String} cls
29565      * A CSS class to apply to the button's main element.
29566      */
29567     
29568     /**
29569      * @cfg {Roo.Template} template (Optional)
29570      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29571      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29572      * require code modifications if required elements (e.g. a button) aren't present.
29573      */
29574
29575     // private
29576     render : function(renderTo){
29577         var btn;
29578         if(this.hideParent){
29579             this.parentEl = Roo.get(renderTo);
29580         }
29581         if(!this.dhconfig){
29582             if(!this.template){
29583                 if(!Roo.Button.buttonTemplate){
29584                     // hideous table template
29585                     Roo.Button.buttonTemplate = new Roo.Template(
29586                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29587                         '<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>',
29588                         "</tr></tbody></table>");
29589                 }
29590                 this.template = Roo.Button.buttonTemplate;
29591             }
29592             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29593             var btnEl = btn.child("button:first");
29594             btnEl.on('focus', this.onFocus, this);
29595             btnEl.on('blur', this.onBlur, this);
29596             if(this.cls){
29597                 btn.addClass(this.cls);
29598             }
29599             if(this.icon){
29600                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29601             }
29602             if(this.iconCls){
29603                 btnEl.addClass(this.iconCls);
29604                 if(!this.cls){
29605                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29606                 }
29607             }
29608             if(this.tabIndex !== undefined){
29609                 btnEl.dom.tabIndex = this.tabIndex;
29610             }
29611             if(this.tooltip){
29612                 if(typeof this.tooltip == 'object'){
29613                     Roo.QuickTips.tips(Roo.apply({
29614                           target: btnEl.id
29615                     }, this.tooltip));
29616                 } else {
29617                     btnEl.dom[this.tooltipType] = this.tooltip;
29618                 }
29619             }
29620         }else{
29621             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29622         }
29623         this.el = btn;
29624         if(this.id){
29625             this.el.dom.id = this.el.id = this.id;
29626         }
29627         if(this.menu){
29628             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29629             this.menu.on("show", this.onMenuShow, this);
29630             this.menu.on("hide", this.onMenuHide, this);
29631         }
29632         btn.addClass("x-btn");
29633         if(Roo.isIE && !Roo.isIE7){
29634             this.autoWidth.defer(1, this);
29635         }else{
29636             this.autoWidth();
29637         }
29638         if(this.handleMouseEvents){
29639             btn.on("mouseover", this.onMouseOver, this);
29640             btn.on("mouseout", this.onMouseOut, this);
29641             btn.on("mousedown", this.onMouseDown, this);
29642         }
29643         btn.on(this.clickEvent, this.onClick, this);
29644         //btn.on("mouseup", this.onMouseUp, this);
29645         if(this.hidden){
29646             this.hide();
29647         }
29648         if(this.disabled){
29649             this.disable();
29650         }
29651         Roo.ButtonToggleMgr.register(this);
29652         if(this.pressed){
29653             this.el.addClass("x-btn-pressed");
29654         }
29655         if(this.repeat){
29656             var repeater = new Roo.util.ClickRepeater(btn,
29657                 typeof this.repeat == "object" ? this.repeat : {}
29658             );
29659             repeater.on("click", this.onClick,  this);
29660         }
29661         
29662         this.fireEvent('render', this);
29663         
29664     },
29665     /**
29666      * Returns the button's underlying element
29667      * @return {Roo.Element} The element
29668      */
29669     getEl : function(){
29670         return this.el;  
29671     },
29672     
29673     /**
29674      * Destroys this Button and removes any listeners.
29675      */
29676     destroy : function(){
29677         Roo.ButtonToggleMgr.unregister(this);
29678         this.el.removeAllListeners();
29679         this.purgeListeners();
29680         this.el.remove();
29681     },
29682
29683     // private
29684     autoWidth : function(){
29685         if(this.el){
29686             this.el.setWidth("auto");
29687             if(Roo.isIE7 && Roo.isStrict){
29688                 var ib = this.el.child('button');
29689                 if(ib && ib.getWidth() > 20){
29690                     ib.clip();
29691                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29692                 }
29693             }
29694             if(this.minWidth){
29695                 if(this.hidden){
29696                     this.el.beginMeasure();
29697                 }
29698                 if(this.el.getWidth() < this.minWidth){
29699                     this.el.setWidth(this.minWidth);
29700                 }
29701                 if(this.hidden){
29702                     this.el.endMeasure();
29703                 }
29704             }
29705         }
29706     },
29707
29708     /**
29709      * Assigns this button's click handler
29710      * @param {Function} handler The function to call when the button is clicked
29711      * @param {Object} scope (optional) Scope for the function passed in
29712      */
29713     setHandler : function(handler, scope){
29714         this.handler = handler;
29715         this.scope = scope;  
29716     },
29717     
29718     /**
29719      * Sets this button's text
29720      * @param {String} text The button text
29721      */
29722     setText : function(text){
29723         this.text = text;
29724         if(this.el){
29725             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29726         }
29727         this.autoWidth();
29728     },
29729     
29730     /**
29731      * Gets the text for this button
29732      * @return {String} The button text
29733      */
29734     getText : function(){
29735         return this.text;  
29736     },
29737     
29738     /**
29739      * Show this button
29740      */
29741     show: function(){
29742         this.hidden = false;
29743         if(this.el){
29744             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29745         }
29746     },
29747     
29748     /**
29749      * Hide this button
29750      */
29751     hide: function(){
29752         this.hidden = true;
29753         if(this.el){
29754             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29755         }
29756     },
29757     
29758     /**
29759      * Convenience function for boolean show/hide
29760      * @param {Boolean} visible True to show, false to hide
29761      */
29762     setVisible: function(visible){
29763         if(visible) {
29764             this.show();
29765         }else{
29766             this.hide();
29767         }
29768     },
29769     
29770     /**
29771      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29772      * @param {Boolean} state (optional) Force a particular state
29773      */
29774     toggle : function(state){
29775         state = state === undefined ? !this.pressed : state;
29776         if(state != this.pressed){
29777             if(state){
29778                 this.el.addClass("x-btn-pressed");
29779                 this.pressed = true;
29780                 this.fireEvent("toggle", this, true);
29781             }else{
29782                 this.el.removeClass("x-btn-pressed");
29783                 this.pressed = false;
29784                 this.fireEvent("toggle", this, false);
29785             }
29786             if(this.toggleHandler){
29787                 this.toggleHandler.call(this.scope || this, this, state);
29788             }
29789         }
29790     },
29791     
29792     /**
29793      * Focus the button
29794      */
29795     focus : function(){
29796         this.el.child('button:first').focus();
29797     },
29798     
29799     /**
29800      * Disable this button
29801      */
29802     disable : function(){
29803         if(this.el){
29804             this.el.addClass("x-btn-disabled");
29805         }
29806         this.disabled = true;
29807     },
29808     
29809     /**
29810      * Enable this button
29811      */
29812     enable : function(){
29813         if(this.el){
29814             this.el.removeClass("x-btn-disabled");
29815         }
29816         this.disabled = false;
29817     },
29818
29819     /**
29820      * Convenience function for boolean enable/disable
29821      * @param {Boolean} enabled True to enable, false to disable
29822      */
29823     setDisabled : function(v){
29824         this[v !== true ? "enable" : "disable"]();
29825     },
29826
29827     // private
29828     onClick : function(e)
29829     {
29830         if(e){
29831             e.preventDefault();
29832         }
29833         if(e.button != 0){
29834             return;
29835         }
29836         if(!this.disabled){
29837             if(this.enableToggle){
29838                 this.toggle();
29839             }
29840             if(this.menu && !this.menu.isVisible()){
29841                 this.menu.show(this.el, this.menuAlign);
29842             }
29843             this.fireEvent("click", this, e);
29844             if(this.handler){
29845                 this.el.removeClass("x-btn-over");
29846                 this.handler.call(this.scope || this, this, e);
29847             }
29848         }
29849     },
29850     // private
29851     onMouseOver : function(e){
29852         if(!this.disabled){
29853             this.el.addClass("x-btn-over");
29854             this.fireEvent('mouseover', this, e);
29855         }
29856     },
29857     // private
29858     onMouseOut : function(e){
29859         if(!e.within(this.el,  true)){
29860             this.el.removeClass("x-btn-over");
29861             this.fireEvent('mouseout', this, e);
29862         }
29863     },
29864     // private
29865     onFocus : function(e){
29866         if(!this.disabled){
29867             this.el.addClass("x-btn-focus");
29868         }
29869     },
29870     // private
29871     onBlur : function(e){
29872         this.el.removeClass("x-btn-focus");
29873     },
29874     // private
29875     onMouseDown : function(e){
29876         if(!this.disabled && e.button == 0){
29877             this.el.addClass("x-btn-click");
29878             Roo.get(document).on('mouseup', this.onMouseUp, this);
29879         }
29880     },
29881     // private
29882     onMouseUp : function(e){
29883         if(e.button == 0){
29884             this.el.removeClass("x-btn-click");
29885             Roo.get(document).un('mouseup', this.onMouseUp, this);
29886         }
29887     },
29888     // private
29889     onMenuShow : function(e){
29890         this.el.addClass("x-btn-menu-active");
29891     },
29892     // private
29893     onMenuHide : function(e){
29894         this.el.removeClass("x-btn-menu-active");
29895     }   
29896 });
29897
29898 // Private utility class used by Button
29899 Roo.ButtonToggleMgr = function(){
29900    var groups = {};
29901    
29902    function toggleGroup(btn, state){
29903        if(state){
29904            var g = groups[btn.toggleGroup];
29905            for(var i = 0, l = g.length; i < l; i++){
29906                if(g[i] != btn){
29907                    g[i].toggle(false);
29908                }
29909            }
29910        }
29911    }
29912    
29913    return {
29914        register : function(btn){
29915            if(!btn.toggleGroup){
29916                return;
29917            }
29918            var g = groups[btn.toggleGroup];
29919            if(!g){
29920                g = groups[btn.toggleGroup] = [];
29921            }
29922            g.push(btn);
29923            btn.on("toggle", toggleGroup);
29924        },
29925        
29926        unregister : function(btn){
29927            if(!btn.toggleGroup){
29928                return;
29929            }
29930            var g = groups[btn.toggleGroup];
29931            if(g){
29932                g.remove(btn);
29933                btn.un("toggle", toggleGroup);
29934            }
29935        }
29936    };
29937 }();/*
29938  * Based on:
29939  * Ext JS Library 1.1.1
29940  * Copyright(c) 2006-2007, Ext JS, LLC.
29941  *
29942  * Originally Released Under LGPL - original licence link has changed is not relivant.
29943  *
29944  * Fork - LGPL
29945  * <script type="text/javascript">
29946  */
29947  
29948 /**
29949  * @class Roo.SplitButton
29950  * @extends Roo.Button
29951  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29952  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29953  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29954  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29955  * @cfg {String} arrowTooltip The title attribute of the arrow
29956  * @constructor
29957  * Create a new menu button
29958  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29959  * @param {Object} config The config object
29960  */
29961 Roo.SplitButton = function(renderTo, config){
29962     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29963     /**
29964      * @event arrowclick
29965      * Fires when this button's arrow is clicked
29966      * @param {SplitButton} this
29967      * @param {EventObject} e The click event
29968      */
29969     this.addEvents({"arrowclick":true});
29970 };
29971
29972 Roo.extend(Roo.SplitButton, Roo.Button, {
29973     render : function(renderTo){
29974         // this is one sweet looking template!
29975         var tpl = new Roo.Template(
29976             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29977             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29978             '<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>',
29979             "</tbody></table></td><td>",
29980             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29981             '<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>',
29982             "</tbody></table></td></tr></table>"
29983         );
29984         var btn = tpl.append(renderTo, [this.text, this.type], true);
29985         var btnEl = btn.child("button");
29986         if(this.cls){
29987             btn.addClass(this.cls);
29988         }
29989         if(this.icon){
29990             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29991         }
29992         if(this.iconCls){
29993             btnEl.addClass(this.iconCls);
29994             if(!this.cls){
29995                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29996             }
29997         }
29998         this.el = btn;
29999         if(this.handleMouseEvents){
30000             btn.on("mouseover", this.onMouseOver, this);
30001             btn.on("mouseout", this.onMouseOut, this);
30002             btn.on("mousedown", this.onMouseDown, this);
30003             btn.on("mouseup", this.onMouseUp, this);
30004         }
30005         btn.on(this.clickEvent, this.onClick, this);
30006         if(this.tooltip){
30007             if(typeof this.tooltip == 'object'){
30008                 Roo.QuickTips.tips(Roo.apply({
30009                       target: btnEl.id
30010                 }, this.tooltip));
30011             } else {
30012                 btnEl.dom[this.tooltipType] = this.tooltip;
30013             }
30014         }
30015         if(this.arrowTooltip){
30016             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30017         }
30018         if(this.hidden){
30019             this.hide();
30020         }
30021         if(this.disabled){
30022             this.disable();
30023         }
30024         if(this.pressed){
30025             this.el.addClass("x-btn-pressed");
30026         }
30027         if(Roo.isIE && !Roo.isIE7){
30028             this.autoWidth.defer(1, this);
30029         }else{
30030             this.autoWidth();
30031         }
30032         if(this.menu){
30033             this.menu.on("show", this.onMenuShow, this);
30034             this.menu.on("hide", this.onMenuHide, this);
30035         }
30036         this.fireEvent('render', this);
30037     },
30038
30039     // private
30040     autoWidth : function(){
30041         if(this.el){
30042             var tbl = this.el.child("table:first");
30043             var tbl2 = this.el.child("table:last");
30044             this.el.setWidth("auto");
30045             tbl.setWidth("auto");
30046             if(Roo.isIE7 && Roo.isStrict){
30047                 var ib = this.el.child('button:first');
30048                 if(ib && ib.getWidth() > 20){
30049                     ib.clip();
30050                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30051                 }
30052             }
30053             if(this.minWidth){
30054                 if(this.hidden){
30055                     this.el.beginMeasure();
30056                 }
30057                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30058                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30059                 }
30060                 if(this.hidden){
30061                     this.el.endMeasure();
30062                 }
30063             }
30064             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30065         } 
30066     },
30067     /**
30068      * Sets this button's click handler
30069      * @param {Function} handler The function to call when the button is clicked
30070      * @param {Object} scope (optional) Scope for the function passed above
30071      */
30072     setHandler : function(handler, scope){
30073         this.handler = handler;
30074         this.scope = scope;  
30075     },
30076     
30077     /**
30078      * Sets this button's arrow click handler
30079      * @param {Function} handler The function to call when the arrow is clicked
30080      * @param {Object} scope (optional) Scope for the function passed above
30081      */
30082     setArrowHandler : function(handler, scope){
30083         this.arrowHandler = handler;
30084         this.scope = scope;  
30085     },
30086     
30087     /**
30088      * Focus the button
30089      */
30090     focus : function(){
30091         if(this.el){
30092             this.el.child("button:first").focus();
30093         }
30094     },
30095
30096     // private
30097     onClick : function(e){
30098         e.preventDefault();
30099         if(!this.disabled){
30100             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30101                 if(this.menu && !this.menu.isVisible()){
30102                     this.menu.show(this.el, this.menuAlign);
30103                 }
30104                 this.fireEvent("arrowclick", this, e);
30105                 if(this.arrowHandler){
30106                     this.arrowHandler.call(this.scope || this, this, e);
30107                 }
30108             }else{
30109                 this.fireEvent("click", this, e);
30110                 if(this.handler){
30111                     this.handler.call(this.scope || this, this, e);
30112                 }
30113             }
30114         }
30115     },
30116     // private
30117     onMouseDown : function(e){
30118         if(!this.disabled){
30119             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30120         }
30121     },
30122     // private
30123     onMouseUp : function(e){
30124         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30125     }   
30126 });
30127
30128
30129 // backwards compat
30130 Roo.MenuButton = Roo.SplitButton;/*
30131  * Based on:
30132  * Ext JS Library 1.1.1
30133  * Copyright(c) 2006-2007, Ext JS, LLC.
30134  *
30135  * Originally Released Under LGPL - original licence link has changed is not relivant.
30136  *
30137  * Fork - LGPL
30138  * <script type="text/javascript">
30139  */
30140
30141 /**
30142  * @class Roo.Toolbar
30143  * Basic Toolbar class.
30144  * @constructor
30145  * Creates a new Toolbar
30146  * @param {Object} container The config object
30147  */ 
30148 Roo.Toolbar = function(container, buttons, config)
30149 {
30150     /// old consturctor format still supported..
30151     if(container instanceof Array){ // omit the container for later rendering
30152         buttons = container;
30153         config = buttons;
30154         container = null;
30155     }
30156     if (typeof(container) == 'object' && container.xtype) {
30157         config = container;
30158         container = config.container;
30159         buttons = config.buttons || []; // not really - use items!!
30160     }
30161     var xitems = [];
30162     if (config && config.items) {
30163         xitems = config.items;
30164         delete config.items;
30165     }
30166     Roo.apply(this, config);
30167     this.buttons = buttons;
30168     
30169     if(container){
30170         this.render(container);
30171     }
30172     this.xitems = xitems;
30173     Roo.each(xitems, function(b) {
30174         this.add(b);
30175     }, this);
30176     
30177 };
30178
30179 Roo.Toolbar.prototype = {
30180     /**
30181      * @cfg {Array} items
30182      * array of button configs or elements to add (will be converted to a MixedCollection)
30183      */
30184     
30185     /**
30186      * @cfg {String/HTMLElement/Element} container
30187      * The id or element that will contain the toolbar
30188      */
30189     // private
30190     render : function(ct){
30191         this.el = Roo.get(ct);
30192         if(this.cls){
30193             this.el.addClass(this.cls);
30194         }
30195         // using a table allows for vertical alignment
30196         // 100% width is needed by Safari...
30197         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30198         this.tr = this.el.child("tr", true);
30199         var autoId = 0;
30200         this.items = new Roo.util.MixedCollection(false, function(o){
30201             return o.id || ("item" + (++autoId));
30202         });
30203         if(this.buttons){
30204             this.add.apply(this, this.buttons);
30205             delete this.buttons;
30206         }
30207     },
30208
30209     /**
30210      * Adds element(s) to the toolbar -- this function takes a variable number of 
30211      * arguments of mixed type and adds them to the toolbar.
30212      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30213      * <ul>
30214      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30215      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30216      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30217      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30218      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30219      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30220      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30221      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30222      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30223      * </ul>
30224      * @param {Mixed} arg2
30225      * @param {Mixed} etc.
30226      */
30227     add : function(){
30228         var a = arguments, l = a.length;
30229         for(var i = 0; i < l; i++){
30230             this._add(a[i]);
30231         }
30232     },
30233     // private..
30234     _add : function(el) {
30235         
30236         if (el.xtype) {
30237             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30238         }
30239         
30240         if (el.applyTo){ // some kind of form field
30241             return this.addField(el);
30242         } 
30243         if (el.render){ // some kind of Toolbar.Item
30244             return this.addItem(el);
30245         }
30246         if (typeof el == "string"){ // string
30247             if(el == "separator" || el == "-"){
30248                 return this.addSeparator();
30249             }
30250             if (el == " "){
30251                 return this.addSpacer();
30252             }
30253             if(el == "->"){
30254                 return this.addFill();
30255             }
30256             return this.addText(el);
30257             
30258         }
30259         if(el.tagName){ // element
30260             return this.addElement(el);
30261         }
30262         if(typeof el == "object"){ // must be button config?
30263             return this.addButton(el);
30264         }
30265         // and now what?!?!
30266         return false;
30267         
30268     },
30269     
30270     /**
30271      * Add an Xtype element
30272      * @param {Object} xtype Xtype Object
30273      * @return {Object} created Object
30274      */
30275     addxtype : function(e){
30276         return this.add(e);  
30277     },
30278     
30279     /**
30280      * Returns the Element for this toolbar.
30281      * @return {Roo.Element}
30282      */
30283     getEl : function(){
30284         return this.el;  
30285     },
30286     
30287     /**
30288      * Adds a separator
30289      * @return {Roo.Toolbar.Item} The separator item
30290      */
30291     addSeparator : function(){
30292         return this.addItem(new Roo.Toolbar.Separator());
30293     },
30294
30295     /**
30296      * Adds a spacer element
30297      * @return {Roo.Toolbar.Spacer} The spacer item
30298      */
30299     addSpacer : function(){
30300         return this.addItem(new Roo.Toolbar.Spacer());
30301     },
30302
30303     /**
30304      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30305      * @return {Roo.Toolbar.Fill} The fill item
30306      */
30307     addFill : function(){
30308         return this.addItem(new Roo.Toolbar.Fill());
30309     },
30310
30311     /**
30312      * Adds any standard HTML element to the toolbar
30313      * @param {String/HTMLElement/Element} el The element or id of the element to add
30314      * @return {Roo.Toolbar.Item} The element's item
30315      */
30316     addElement : function(el){
30317         return this.addItem(new Roo.Toolbar.Item(el));
30318     },
30319     /**
30320      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30321      * @type Roo.util.MixedCollection  
30322      */
30323     items : false,
30324      
30325     /**
30326      * Adds any Toolbar.Item or subclass
30327      * @param {Roo.Toolbar.Item} item
30328      * @return {Roo.Toolbar.Item} The item
30329      */
30330     addItem : function(item){
30331         var td = this.nextBlock();
30332         item.render(td);
30333         this.items.add(item);
30334         return item;
30335     },
30336     
30337     /**
30338      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30339      * @param {Object/Array} config A button config or array of configs
30340      * @return {Roo.Toolbar.Button/Array}
30341      */
30342     addButton : function(config){
30343         if(config instanceof Array){
30344             var buttons = [];
30345             for(var i = 0, len = config.length; i < len; i++) {
30346                 buttons.push(this.addButton(config[i]));
30347             }
30348             return buttons;
30349         }
30350         var b = config;
30351         if(!(config instanceof Roo.Toolbar.Button)){
30352             b = config.split ?
30353                 new Roo.Toolbar.SplitButton(config) :
30354                 new Roo.Toolbar.Button(config);
30355         }
30356         var td = this.nextBlock();
30357         b.render(td);
30358         this.items.add(b);
30359         return b;
30360     },
30361     
30362     /**
30363      * Adds text to the toolbar
30364      * @param {String} text The text to add
30365      * @return {Roo.Toolbar.Item} The element's item
30366      */
30367     addText : function(text){
30368         return this.addItem(new Roo.Toolbar.TextItem(text));
30369     },
30370     
30371     /**
30372      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30373      * @param {Number} index The index where the item is to be inserted
30374      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30375      * @return {Roo.Toolbar.Button/Item}
30376      */
30377     insertButton : function(index, item){
30378         if(item instanceof Array){
30379             var buttons = [];
30380             for(var i = 0, len = item.length; i < len; i++) {
30381                buttons.push(this.insertButton(index + i, item[i]));
30382             }
30383             return buttons;
30384         }
30385         if (!(item instanceof Roo.Toolbar.Button)){
30386            item = new Roo.Toolbar.Button(item);
30387         }
30388         var td = document.createElement("td");
30389         this.tr.insertBefore(td, this.tr.childNodes[index]);
30390         item.render(td);
30391         this.items.insert(index, item);
30392         return item;
30393     },
30394     
30395     /**
30396      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30397      * @param {Object} config
30398      * @return {Roo.Toolbar.Item} The element's item
30399      */
30400     addDom : function(config, returnEl){
30401         var td = this.nextBlock();
30402         Roo.DomHelper.overwrite(td, config);
30403         var ti = new Roo.Toolbar.Item(td.firstChild);
30404         ti.render(td);
30405         this.items.add(ti);
30406         return ti;
30407     },
30408
30409     /**
30410      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30411      * @type Roo.util.MixedCollection  
30412      */
30413     fields : false,
30414     
30415     /**
30416      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30417      * Note: the field should not have been rendered yet. For a field that has already been
30418      * rendered, use {@link #addElement}.
30419      * @param {Roo.form.Field} field
30420      * @return {Roo.ToolbarItem}
30421      */
30422      
30423       
30424     addField : function(field) {
30425         if (!this.fields) {
30426             var autoId = 0;
30427             this.fields = new Roo.util.MixedCollection(false, function(o){
30428                 return o.id || ("item" + (++autoId));
30429             });
30430
30431         }
30432         
30433         var td = this.nextBlock();
30434         field.render(td);
30435         var ti = new Roo.Toolbar.Item(td.firstChild);
30436         ti.render(td);
30437         this.items.add(ti);
30438         this.fields.add(field);
30439         return ti;
30440     },
30441     /**
30442      * Hide the toolbar
30443      * @method hide
30444      */
30445      
30446       
30447     hide : function()
30448     {
30449         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30450         this.el.child('div').hide();
30451     },
30452     /**
30453      * Show the toolbar
30454      * @method show
30455      */
30456     show : function()
30457     {
30458         this.el.child('div').show();
30459     },
30460       
30461     // private
30462     nextBlock : function(){
30463         var td = document.createElement("td");
30464         this.tr.appendChild(td);
30465         return td;
30466     },
30467
30468     // private
30469     destroy : function(){
30470         if(this.items){ // rendered?
30471             Roo.destroy.apply(Roo, this.items.items);
30472         }
30473         if(this.fields){ // rendered?
30474             Roo.destroy.apply(Roo, this.fields.items);
30475         }
30476         Roo.Element.uncache(this.el, this.tr);
30477     }
30478 };
30479
30480 /**
30481  * @class Roo.Toolbar.Item
30482  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30483  * @constructor
30484  * Creates a new Item
30485  * @param {HTMLElement} el 
30486  */
30487 Roo.Toolbar.Item = function(el){
30488     var cfg = {};
30489     if (typeof (el.xtype) != 'undefined') {
30490         cfg = el;
30491         el = cfg.el;
30492     }
30493     
30494     this.el = Roo.getDom(el);
30495     this.id = Roo.id(this.el);
30496     this.hidden = false;
30497     
30498     this.addEvents({
30499          /**
30500              * @event render
30501              * Fires when the button is rendered
30502              * @param {Button} this
30503              */
30504         'render': true
30505     });
30506     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30507 };
30508 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30509 //Roo.Toolbar.Item.prototype = {
30510     
30511     /**
30512      * Get this item's HTML Element
30513      * @return {HTMLElement}
30514      */
30515     getEl : function(){
30516        return this.el;  
30517     },
30518
30519     // private
30520     render : function(td){
30521         
30522          this.td = td;
30523         td.appendChild(this.el);
30524         
30525         this.fireEvent('render', this);
30526     },
30527     
30528     /**
30529      * Removes and destroys this item.
30530      */
30531     destroy : function(){
30532         this.td.parentNode.removeChild(this.td);
30533     },
30534     
30535     /**
30536      * Shows this item.
30537      */
30538     show: function(){
30539         this.hidden = false;
30540         this.td.style.display = "";
30541     },
30542     
30543     /**
30544      * Hides this item.
30545      */
30546     hide: function(){
30547         this.hidden = true;
30548         this.td.style.display = "none";
30549     },
30550     
30551     /**
30552      * Convenience function for boolean show/hide.
30553      * @param {Boolean} visible true to show/false to hide
30554      */
30555     setVisible: function(visible){
30556         if(visible) {
30557             this.show();
30558         }else{
30559             this.hide();
30560         }
30561     },
30562     
30563     /**
30564      * Try to focus this item.
30565      */
30566     focus : function(){
30567         Roo.fly(this.el).focus();
30568     },
30569     
30570     /**
30571      * Disables this item.
30572      */
30573     disable : function(){
30574         Roo.fly(this.td).addClass("x-item-disabled");
30575         this.disabled = true;
30576         this.el.disabled = true;
30577     },
30578     
30579     /**
30580      * Enables this item.
30581      */
30582     enable : function(){
30583         Roo.fly(this.td).removeClass("x-item-disabled");
30584         this.disabled = false;
30585         this.el.disabled = false;
30586     }
30587 });
30588
30589
30590 /**
30591  * @class Roo.Toolbar.Separator
30592  * @extends Roo.Toolbar.Item
30593  * A simple toolbar separator class
30594  * @constructor
30595  * Creates a new Separator
30596  */
30597 Roo.Toolbar.Separator = function(cfg){
30598     
30599     var s = document.createElement("span");
30600     s.className = "ytb-sep";
30601     if (cfg) {
30602         cfg.el = s;
30603     }
30604     
30605     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30606 };
30607 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30608     enable:Roo.emptyFn,
30609     disable:Roo.emptyFn,
30610     focus:Roo.emptyFn
30611 });
30612
30613 /**
30614  * @class Roo.Toolbar.Spacer
30615  * @extends Roo.Toolbar.Item
30616  * A simple element that adds extra horizontal space to a toolbar.
30617  * @constructor
30618  * Creates a new Spacer
30619  */
30620 Roo.Toolbar.Spacer = function(cfg){
30621     var s = document.createElement("div");
30622     s.className = "ytb-spacer";
30623     if (cfg) {
30624         cfg.el = s;
30625     }
30626     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30627 };
30628 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30629     enable:Roo.emptyFn,
30630     disable:Roo.emptyFn,
30631     focus:Roo.emptyFn
30632 });
30633
30634 /**
30635  * @class Roo.Toolbar.Fill
30636  * @extends Roo.Toolbar.Spacer
30637  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30638  * @constructor
30639  * Creates a new Spacer
30640  */
30641 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30642     // private
30643     render : function(td){
30644         td.style.width = '100%';
30645         Roo.Toolbar.Fill.superclass.render.call(this, td);
30646     }
30647 });
30648
30649 /**
30650  * @class Roo.Toolbar.TextItem
30651  * @extends Roo.Toolbar.Item
30652  * A simple class that renders text directly into a toolbar.
30653  * @constructor
30654  * Creates a new TextItem
30655  * @cfg {string} text 
30656  */
30657 Roo.Toolbar.TextItem = function(cfg){
30658     var  text = cfg || "";
30659     if (typeof(cfg) == 'object') {
30660         text = cfg.text || "";
30661     }  else {
30662         cfg = null;
30663     }
30664     var s = document.createElement("span");
30665     s.className = "ytb-text";
30666     s.innerHTML = text;
30667     if (cfg) {
30668         cfg.el  = s;
30669     }
30670     
30671     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30672 };
30673 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30674     
30675      
30676     enable:Roo.emptyFn,
30677     disable:Roo.emptyFn,
30678     focus:Roo.emptyFn
30679 });
30680
30681 /**
30682  * @class Roo.Toolbar.Button
30683  * @extends Roo.Button
30684  * A button that renders into a toolbar.
30685  * @constructor
30686  * Creates a new Button
30687  * @param {Object} config A standard {@link Roo.Button} config object
30688  */
30689 Roo.Toolbar.Button = function(config){
30690     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30691 };
30692 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30693     render : function(td){
30694         this.td = td;
30695         Roo.Toolbar.Button.superclass.render.call(this, td);
30696     },
30697     
30698     /**
30699      * Removes and destroys this button
30700      */
30701     destroy : function(){
30702         Roo.Toolbar.Button.superclass.destroy.call(this);
30703         this.td.parentNode.removeChild(this.td);
30704     },
30705     
30706     /**
30707      * Shows this button
30708      */
30709     show: function(){
30710         this.hidden = false;
30711         this.td.style.display = "";
30712     },
30713     
30714     /**
30715      * Hides this button
30716      */
30717     hide: function(){
30718         this.hidden = true;
30719         this.td.style.display = "none";
30720     },
30721
30722     /**
30723      * Disables this item
30724      */
30725     disable : function(){
30726         Roo.fly(this.td).addClass("x-item-disabled");
30727         this.disabled = true;
30728     },
30729
30730     /**
30731      * Enables this item
30732      */
30733     enable : function(){
30734         Roo.fly(this.td).removeClass("x-item-disabled");
30735         this.disabled = false;
30736     }
30737 });
30738 // backwards compat
30739 Roo.ToolbarButton = Roo.Toolbar.Button;
30740
30741 /**
30742  * @class Roo.Toolbar.SplitButton
30743  * @extends Roo.SplitButton
30744  * A menu button that renders into a toolbar.
30745  * @constructor
30746  * Creates a new SplitButton
30747  * @param {Object} config A standard {@link Roo.SplitButton} config object
30748  */
30749 Roo.Toolbar.SplitButton = function(config){
30750     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30751 };
30752 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30753     render : function(td){
30754         this.td = td;
30755         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30756     },
30757     
30758     /**
30759      * Removes and destroys this button
30760      */
30761     destroy : function(){
30762         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30763         this.td.parentNode.removeChild(this.td);
30764     },
30765     
30766     /**
30767      * Shows this button
30768      */
30769     show: function(){
30770         this.hidden = false;
30771         this.td.style.display = "";
30772     },
30773     
30774     /**
30775      * Hides this button
30776      */
30777     hide: function(){
30778         this.hidden = true;
30779         this.td.style.display = "none";
30780     }
30781 });
30782
30783 // backwards compat
30784 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30785  * Based on:
30786  * Ext JS Library 1.1.1
30787  * Copyright(c) 2006-2007, Ext JS, LLC.
30788  *
30789  * Originally Released Under LGPL - original licence link has changed is not relivant.
30790  *
30791  * Fork - LGPL
30792  * <script type="text/javascript">
30793  */
30794  
30795 /**
30796  * @class Roo.PagingToolbar
30797  * @extends Roo.Toolbar
30798  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30799  * @constructor
30800  * Create a new PagingToolbar
30801  * @param {Object} config The config object
30802  */
30803 Roo.PagingToolbar = function(el, ds, config)
30804 {
30805     // old args format still supported... - xtype is prefered..
30806     if (typeof(el) == 'object' && el.xtype) {
30807         // created from xtype...
30808         config = el;
30809         ds = el.dataSource;
30810         el = config.container;
30811     }
30812     var items = [];
30813     if (config.items) {
30814         items = config.items;
30815         config.items = [];
30816     }
30817     
30818     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30819     this.ds = ds;
30820     this.cursor = 0;
30821     this.renderButtons(this.el);
30822     this.bind(ds);
30823     
30824     // supprot items array.
30825    
30826     Roo.each(items, function(e) {
30827         this.add(Roo.factory(e));
30828     },this);
30829     
30830 };
30831
30832 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30833     /**
30834      * @cfg {Roo.data.Store} dataSource
30835      * The underlying data store providing the paged data
30836      */
30837     /**
30838      * @cfg {String/HTMLElement/Element} container
30839      * container The id or element that will contain the toolbar
30840      */
30841     /**
30842      * @cfg {Boolean} displayInfo
30843      * True to display the displayMsg (defaults to false)
30844      */
30845     /**
30846      * @cfg {Number} pageSize
30847      * The number of records to display per page (defaults to 20)
30848      */
30849     pageSize: 20,
30850     /**
30851      * @cfg {String} displayMsg
30852      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30853      */
30854     displayMsg : 'Displaying {0} - {1} of {2}',
30855     /**
30856      * @cfg {String} emptyMsg
30857      * The message to display when no records are found (defaults to "No data to display")
30858      */
30859     emptyMsg : 'No data to display',
30860     /**
30861      * Customizable piece of the default paging text (defaults to "Page")
30862      * @type String
30863      */
30864     beforePageText : "Page",
30865     /**
30866      * Customizable piece of the default paging text (defaults to "of %0")
30867      * @type String
30868      */
30869     afterPageText : "of {0}",
30870     /**
30871      * Customizable piece of the default paging text (defaults to "First Page")
30872      * @type String
30873      */
30874     firstText : "First Page",
30875     /**
30876      * Customizable piece of the default paging text (defaults to "Previous Page")
30877      * @type String
30878      */
30879     prevText : "Previous Page",
30880     /**
30881      * Customizable piece of the default paging text (defaults to "Next Page")
30882      * @type String
30883      */
30884     nextText : "Next Page",
30885     /**
30886      * Customizable piece of the default paging text (defaults to "Last Page")
30887      * @type String
30888      */
30889     lastText : "Last Page",
30890     /**
30891      * Customizable piece of the default paging text (defaults to "Refresh")
30892      * @type String
30893      */
30894     refreshText : "Refresh",
30895
30896     // private
30897     renderButtons : function(el){
30898         Roo.PagingToolbar.superclass.render.call(this, el);
30899         this.first = this.addButton({
30900             tooltip: this.firstText,
30901             cls: "x-btn-icon x-grid-page-first",
30902             disabled: true,
30903             handler: this.onClick.createDelegate(this, ["first"])
30904         });
30905         this.prev = this.addButton({
30906             tooltip: this.prevText,
30907             cls: "x-btn-icon x-grid-page-prev",
30908             disabled: true,
30909             handler: this.onClick.createDelegate(this, ["prev"])
30910         });
30911         //this.addSeparator();
30912         this.add(this.beforePageText);
30913         this.field = Roo.get(this.addDom({
30914            tag: "input",
30915            type: "text",
30916            size: "3",
30917            value: "1",
30918            cls: "x-grid-page-number"
30919         }).el);
30920         this.field.on("keydown", this.onPagingKeydown, this);
30921         this.field.on("focus", function(){this.dom.select();});
30922         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30923         this.field.setHeight(18);
30924         //this.addSeparator();
30925         this.next = this.addButton({
30926             tooltip: this.nextText,
30927             cls: "x-btn-icon x-grid-page-next",
30928             disabled: true,
30929             handler: this.onClick.createDelegate(this, ["next"])
30930         });
30931         this.last = this.addButton({
30932             tooltip: this.lastText,
30933             cls: "x-btn-icon x-grid-page-last",
30934             disabled: true,
30935             handler: this.onClick.createDelegate(this, ["last"])
30936         });
30937         //this.addSeparator();
30938         this.loading = this.addButton({
30939             tooltip: this.refreshText,
30940             cls: "x-btn-icon x-grid-loading",
30941             handler: this.onClick.createDelegate(this, ["refresh"])
30942         });
30943
30944         if(this.displayInfo){
30945             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30946         }
30947     },
30948
30949     // private
30950     updateInfo : function(){
30951         if(this.displayEl){
30952             var count = this.ds.getCount();
30953             var msg = count == 0 ?
30954                 this.emptyMsg :
30955                 String.format(
30956                     this.displayMsg,
30957                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30958                 );
30959             this.displayEl.update(msg);
30960         }
30961     },
30962
30963     // private
30964     onLoad : function(ds, r, o){
30965        this.cursor = o.params ? o.params.start : 0;
30966        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30967
30968        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30969        this.field.dom.value = ap;
30970        this.first.setDisabled(ap == 1);
30971        this.prev.setDisabled(ap == 1);
30972        this.next.setDisabled(ap == ps);
30973        this.last.setDisabled(ap == ps);
30974        this.loading.enable();
30975        this.updateInfo();
30976     },
30977
30978     // private
30979     getPageData : function(){
30980         var total = this.ds.getTotalCount();
30981         return {
30982             total : total,
30983             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30984             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30985         };
30986     },
30987
30988     // private
30989     onLoadError : function(){
30990         this.loading.enable();
30991     },
30992
30993     // private
30994     onPagingKeydown : function(e){
30995         var k = e.getKey();
30996         var d = this.getPageData();
30997         if(k == e.RETURN){
30998             var v = this.field.dom.value, pageNum;
30999             if(!v || isNaN(pageNum = parseInt(v, 10))){
31000                 this.field.dom.value = d.activePage;
31001                 return;
31002             }
31003             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31004             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31005             e.stopEvent();
31006         }
31007         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))
31008         {
31009           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31010           this.field.dom.value = pageNum;
31011           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31012           e.stopEvent();
31013         }
31014         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31015         {
31016           var v = this.field.dom.value, pageNum; 
31017           var increment = (e.shiftKey) ? 10 : 1;
31018           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31019             increment *= -1;
31020           }
31021           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31022             this.field.dom.value = d.activePage;
31023             return;
31024           }
31025           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31026           {
31027             this.field.dom.value = parseInt(v, 10) + increment;
31028             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31029             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31030           }
31031           e.stopEvent();
31032         }
31033     },
31034
31035     // private
31036     beforeLoad : function(){
31037         if(this.loading){
31038             this.loading.disable();
31039         }
31040     },
31041
31042     // private
31043     onClick : function(which){
31044         var ds = this.ds;
31045         switch(which){
31046             case "first":
31047                 ds.load({params:{start: 0, limit: this.pageSize}});
31048             break;
31049             case "prev":
31050                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31051             break;
31052             case "next":
31053                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31054             break;
31055             case "last":
31056                 var total = ds.getTotalCount();
31057                 var extra = total % this.pageSize;
31058                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31059                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31060             break;
31061             case "refresh":
31062                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31063             break;
31064         }
31065     },
31066
31067     /**
31068      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31069      * @param {Roo.data.Store} store The data store to unbind
31070      */
31071     unbind : function(ds){
31072         ds.un("beforeload", this.beforeLoad, this);
31073         ds.un("load", this.onLoad, this);
31074         ds.un("loadexception", this.onLoadError, this);
31075         ds.un("remove", this.updateInfo, this);
31076         ds.un("add", this.updateInfo, this);
31077         this.ds = undefined;
31078     },
31079
31080     /**
31081      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31082      * @param {Roo.data.Store} store The data store to bind
31083      */
31084     bind : function(ds){
31085         ds.on("beforeload", this.beforeLoad, this);
31086         ds.on("load", this.onLoad, this);
31087         ds.on("loadexception", this.onLoadError, this);
31088         ds.on("remove", this.updateInfo, this);
31089         ds.on("add", this.updateInfo, this);
31090         this.ds = ds;
31091     }
31092 });/*
31093  * Based on:
31094  * Ext JS Library 1.1.1
31095  * Copyright(c) 2006-2007, Ext JS, LLC.
31096  *
31097  * Originally Released Under LGPL - original licence link has changed is not relivant.
31098  *
31099  * Fork - LGPL
31100  * <script type="text/javascript">
31101  */
31102
31103 /**
31104  * @class Roo.Resizable
31105  * @extends Roo.util.Observable
31106  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31107  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31108  * 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
31109  * the element will be wrapped for you automatically.</p>
31110  * <p>Here is the list of valid resize handles:</p>
31111  * <pre>
31112 Value   Description
31113 ------  -------------------
31114  'n'     north
31115  's'     south
31116  'e'     east
31117  'w'     west
31118  'nw'    northwest
31119  'sw'    southwest
31120  'se'    southeast
31121  'ne'    northeast
31122  'hd'    horizontal drag
31123  'all'   all
31124 </pre>
31125  * <p>Here's an example showing the creation of a typical Resizable:</p>
31126  * <pre><code>
31127 var resizer = new Roo.Resizable("element-id", {
31128     handles: 'all',
31129     minWidth: 200,
31130     minHeight: 100,
31131     maxWidth: 500,
31132     maxHeight: 400,
31133     pinned: true
31134 });
31135 resizer.on("resize", myHandler);
31136 </code></pre>
31137  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31138  * resizer.east.setDisplayed(false);</p>
31139  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31140  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31141  * resize operation's new size (defaults to [0, 0])
31142  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31143  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31144  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31145  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31146  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31147  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31148  * @cfg {Number} width The width of the element in pixels (defaults to null)
31149  * @cfg {Number} height The height of the element in pixels (defaults to null)
31150  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31151  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31152  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31153  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31154  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31155  * in favor of the handles config option (defaults to false)
31156  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31157  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31158  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31159  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31160  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31161  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31162  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31163  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31164  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31165  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31166  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31167  * @constructor
31168  * Create a new resizable component
31169  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31170  * @param {Object} config configuration options
31171   */
31172 Roo.Resizable = function(el, config)
31173 {
31174     this.el = Roo.get(el);
31175
31176     if(config && config.wrap){
31177         config.resizeChild = this.el;
31178         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31179         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31180         this.el.setStyle("overflow", "hidden");
31181         this.el.setPositioning(config.resizeChild.getPositioning());
31182         config.resizeChild.clearPositioning();
31183         if(!config.width || !config.height){
31184             var csize = config.resizeChild.getSize();
31185             this.el.setSize(csize.width, csize.height);
31186         }
31187         if(config.pinned && !config.adjustments){
31188             config.adjustments = "auto";
31189         }
31190     }
31191
31192     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31193     this.proxy.unselectable();
31194     this.proxy.enableDisplayMode('block');
31195
31196     Roo.apply(this, config);
31197
31198     if(this.pinned){
31199         this.disableTrackOver = true;
31200         this.el.addClass("x-resizable-pinned");
31201     }
31202     // if the element isn't positioned, make it relative
31203     var position = this.el.getStyle("position");
31204     if(position != "absolute" && position != "fixed"){
31205         this.el.setStyle("position", "relative");
31206     }
31207     if(!this.handles){ // no handles passed, must be legacy style
31208         this.handles = 's,e,se';
31209         if(this.multiDirectional){
31210             this.handles += ',n,w';
31211         }
31212     }
31213     if(this.handles == "all"){
31214         this.handles = "n s e w ne nw se sw";
31215     }
31216     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31217     var ps = Roo.Resizable.positions;
31218     for(var i = 0, len = hs.length; i < len; i++){
31219         if(hs[i] && ps[hs[i]]){
31220             var pos = ps[hs[i]];
31221             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31222         }
31223     }
31224     // legacy
31225     this.corner = this.southeast;
31226     
31227     // updateBox = the box can move..
31228     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31229         this.updateBox = true;
31230     }
31231
31232     this.activeHandle = null;
31233
31234     if(this.resizeChild){
31235         if(typeof this.resizeChild == "boolean"){
31236             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31237         }else{
31238             this.resizeChild = Roo.get(this.resizeChild, true);
31239         }
31240     }
31241     
31242     if(this.adjustments == "auto"){
31243         var rc = this.resizeChild;
31244         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31245         if(rc && (hw || hn)){
31246             rc.position("relative");
31247             rc.setLeft(hw ? hw.el.getWidth() : 0);
31248             rc.setTop(hn ? hn.el.getHeight() : 0);
31249         }
31250         this.adjustments = [
31251             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31252             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31253         ];
31254     }
31255
31256     if(this.draggable){
31257         this.dd = this.dynamic ?
31258             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31259         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31260     }
31261
31262     // public events
31263     this.addEvents({
31264         /**
31265          * @event beforeresize
31266          * Fired before resize is allowed. Set enabled to false to cancel resize.
31267          * @param {Roo.Resizable} this
31268          * @param {Roo.EventObject} e The mousedown event
31269          */
31270         "beforeresize" : true,
31271         /**
31272          * @event resizing
31273          * Fired a resizing.
31274          * @param {Roo.Resizable} this
31275          * @param {Number} x The new x position
31276          * @param {Number} y The new y position
31277          * @param {Number} w The new w width
31278          * @param {Number} h The new h hight
31279          * @param {Roo.EventObject} e The mouseup event
31280          */
31281         "resizing" : true,
31282         /**
31283          * @event resize
31284          * Fired after a resize.
31285          * @param {Roo.Resizable} this
31286          * @param {Number} width The new width
31287          * @param {Number} height The new height
31288          * @param {Roo.EventObject} e The mouseup event
31289          */
31290         "resize" : true
31291     });
31292
31293     if(this.width !== null && this.height !== null){
31294         this.resizeTo(this.width, this.height);
31295     }else{
31296         this.updateChildSize();
31297     }
31298     if(Roo.isIE){
31299         this.el.dom.style.zoom = 1;
31300     }
31301     Roo.Resizable.superclass.constructor.call(this);
31302 };
31303
31304 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31305         resizeChild : false,
31306         adjustments : [0, 0],
31307         minWidth : 5,
31308         minHeight : 5,
31309         maxWidth : 10000,
31310         maxHeight : 10000,
31311         enabled : true,
31312         animate : false,
31313         duration : .35,
31314         dynamic : false,
31315         handles : false,
31316         multiDirectional : false,
31317         disableTrackOver : false,
31318         easing : 'easeOutStrong',
31319         widthIncrement : 0,
31320         heightIncrement : 0,
31321         pinned : false,
31322         width : null,
31323         height : null,
31324         preserveRatio : false,
31325         transparent: false,
31326         minX: 0,
31327         minY: 0,
31328         draggable: false,
31329
31330         /**
31331          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31332          */
31333         constrainTo: undefined,
31334         /**
31335          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31336          */
31337         resizeRegion: undefined,
31338
31339
31340     /**
31341      * Perform a manual resize
31342      * @param {Number} width
31343      * @param {Number} height
31344      */
31345     resizeTo : function(width, height){
31346         this.el.setSize(width, height);
31347         this.updateChildSize();
31348         this.fireEvent("resize", this, width, height, null);
31349     },
31350
31351     // private
31352     startSizing : function(e, handle){
31353         this.fireEvent("beforeresize", this, e);
31354         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31355
31356             if(!this.overlay){
31357                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31358                 this.overlay.unselectable();
31359                 this.overlay.enableDisplayMode("block");
31360                 this.overlay.on("mousemove", this.onMouseMove, this);
31361                 this.overlay.on("mouseup", this.onMouseUp, this);
31362             }
31363             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31364
31365             this.resizing = true;
31366             this.startBox = this.el.getBox();
31367             this.startPoint = e.getXY();
31368             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31369                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31370
31371             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31372             this.overlay.show();
31373
31374             if(this.constrainTo) {
31375                 var ct = Roo.get(this.constrainTo);
31376                 this.resizeRegion = ct.getRegion().adjust(
31377                     ct.getFrameWidth('t'),
31378                     ct.getFrameWidth('l'),
31379                     -ct.getFrameWidth('b'),
31380                     -ct.getFrameWidth('r')
31381                 );
31382             }
31383
31384             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31385             this.proxy.show();
31386             this.proxy.setBox(this.startBox);
31387             if(!this.dynamic){
31388                 this.proxy.setStyle('visibility', 'visible');
31389             }
31390         }
31391     },
31392
31393     // private
31394     onMouseDown : function(handle, e){
31395         if(this.enabled){
31396             e.stopEvent();
31397             this.activeHandle = handle;
31398             this.startSizing(e, handle);
31399         }
31400     },
31401
31402     // private
31403     onMouseUp : function(e){
31404         var size = this.resizeElement();
31405         this.resizing = false;
31406         this.handleOut();
31407         this.overlay.hide();
31408         this.proxy.hide();
31409         this.fireEvent("resize", this, size.width, size.height, e);
31410     },
31411
31412     // private
31413     updateChildSize : function(){
31414         
31415         if(this.resizeChild){
31416             var el = this.el;
31417             var child = this.resizeChild;
31418             var adj = this.adjustments;
31419             if(el.dom.offsetWidth){
31420                 var b = el.getSize(true);
31421                 child.setSize(b.width+adj[0], b.height+adj[1]);
31422             }
31423             // Second call here for IE
31424             // The first call enables instant resizing and
31425             // the second call corrects scroll bars if they
31426             // exist
31427             if(Roo.isIE){
31428                 setTimeout(function(){
31429                     if(el.dom.offsetWidth){
31430                         var b = el.getSize(true);
31431                         child.setSize(b.width+adj[0], b.height+adj[1]);
31432                     }
31433                 }, 10);
31434             }
31435         }
31436     },
31437
31438     // private
31439     snap : function(value, inc, min){
31440         if(!inc || !value) {
31441             return value;
31442         }
31443         var newValue = value;
31444         var m = value % inc;
31445         if(m > 0){
31446             if(m > (inc/2)){
31447                 newValue = value + (inc-m);
31448             }else{
31449                 newValue = value - m;
31450             }
31451         }
31452         return Math.max(min, newValue);
31453     },
31454
31455     // private
31456     resizeElement : function(){
31457         var box = this.proxy.getBox();
31458         if(this.updateBox){
31459             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31460         }else{
31461             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31462         }
31463         this.updateChildSize();
31464         if(!this.dynamic){
31465             this.proxy.hide();
31466         }
31467         return box;
31468     },
31469
31470     // private
31471     constrain : function(v, diff, m, mx){
31472         if(v - diff < m){
31473             diff = v - m;
31474         }else if(v - diff > mx){
31475             diff = mx - v;
31476         }
31477         return diff;
31478     },
31479
31480     // private
31481     onMouseMove : function(e){
31482         
31483         if(this.enabled){
31484             try{// try catch so if something goes wrong the user doesn't get hung
31485
31486             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31487                 return;
31488             }
31489
31490             //var curXY = this.startPoint;
31491             var curSize = this.curSize || this.startBox;
31492             var x = this.startBox.x, y = this.startBox.y;
31493             var ox = x, oy = y;
31494             var w = curSize.width, h = curSize.height;
31495             var ow = w, oh = h;
31496             var mw = this.minWidth, mh = this.minHeight;
31497             var mxw = this.maxWidth, mxh = this.maxHeight;
31498             var wi = this.widthIncrement;
31499             var hi = this.heightIncrement;
31500
31501             var eventXY = e.getXY();
31502             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31503             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31504
31505             var pos = this.activeHandle.position;
31506
31507             switch(pos){
31508                 case "east":
31509                     w += diffX;
31510                     w = Math.min(Math.max(mw, w), mxw);
31511                     break;
31512              
31513                 case "south":
31514                     h += diffY;
31515                     h = Math.min(Math.max(mh, h), mxh);
31516                     break;
31517                 case "southeast":
31518                     w += diffX;
31519                     h += diffY;
31520                     w = Math.min(Math.max(mw, w), mxw);
31521                     h = Math.min(Math.max(mh, h), mxh);
31522                     break;
31523                 case "north":
31524                     diffY = this.constrain(h, diffY, mh, mxh);
31525                     y += diffY;
31526                     h -= diffY;
31527                     break;
31528                 case "hdrag":
31529                     
31530                     if (wi) {
31531                         var adiffX = Math.abs(diffX);
31532                         var sub = (adiffX % wi); // how much 
31533                         if (sub > (wi/2)) { // far enough to snap
31534                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31535                         } else {
31536                             // remove difference.. 
31537                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31538                         }
31539                     }
31540                     x += diffX;
31541                     x = Math.max(this.minX, x);
31542                     break;
31543                 case "west":
31544                     diffX = this.constrain(w, diffX, mw, mxw);
31545                     x += diffX;
31546                     w -= diffX;
31547                     break;
31548                 case "northeast":
31549                     w += diffX;
31550                     w = Math.min(Math.max(mw, w), mxw);
31551                     diffY = this.constrain(h, diffY, mh, mxh);
31552                     y += diffY;
31553                     h -= diffY;
31554                     break;
31555                 case "northwest":
31556                     diffX = this.constrain(w, diffX, mw, mxw);
31557                     diffY = this.constrain(h, diffY, mh, mxh);
31558                     y += diffY;
31559                     h -= diffY;
31560                     x += diffX;
31561                     w -= diffX;
31562                     break;
31563                case "southwest":
31564                     diffX = this.constrain(w, diffX, mw, mxw);
31565                     h += diffY;
31566                     h = Math.min(Math.max(mh, h), mxh);
31567                     x += diffX;
31568                     w -= diffX;
31569                     break;
31570             }
31571
31572             var sw = this.snap(w, wi, mw);
31573             var sh = this.snap(h, hi, mh);
31574             if(sw != w || sh != h){
31575                 switch(pos){
31576                     case "northeast":
31577                         y -= sh - h;
31578                     break;
31579                     case "north":
31580                         y -= sh - h;
31581                         break;
31582                     case "southwest":
31583                         x -= sw - w;
31584                     break;
31585                     case "west":
31586                         x -= sw - w;
31587                         break;
31588                     case "northwest":
31589                         x -= sw - w;
31590                         y -= sh - h;
31591                     break;
31592                 }
31593                 w = sw;
31594                 h = sh;
31595             }
31596
31597             if(this.preserveRatio){
31598                 switch(pos){
31599                     case "southeast":
31600                     case "east":
31601                         h = oh * (w/ow);
31602                         h = Math.min(Math.max(mh, h), mxh);
31603                         w = ow * (h/oh);
31604                        break;
31605                     case "south":
31606                         w = ow * (h/oh);
31607                         w = Math.min(Math.max(mw, w), mxw);
31608                         h = oh * (w/ow);
31609                         break;
31610                     case "northeast":
31611                         w = ow * (h/oh);
31612                         w = Math.min(Math.max(mw, w), mxw);
31613                         h = oh * (w/ow);
31614                     break;
31615                     case "north":
31616                         var tw = w;
31617                         w = ow * (h/oh);
31618                         w = Math.min(Math.max(mw, w), mxw);
31619                         h = oh * (w/ow);
31620                         x += (tw - w) / 2;
31621                         break;
31622                     case "southwest":
31623                         h = oh * (w/ow);
31624                         h = Math.min(Math.max(mh, h), mxh);
31625                         var tw = w;
31626                         w = ow * (h/oh);
31627                         x += tw - w;
31628                         break;
31629                     case "west":
31630                         var th = h;
31631                         h = oh * (w/ow);
31632                         h = Math.min(Math.max(mh, h), mxh);
31633                         y += (th - h) / 2;
31634                         var tw = w;
31635                         w = ow * (h/oh);
31636                         x += tw - w;
31637                        break;
31638                     case "northwest":
31639                         var tw = w;
31640                         var th = h;
31641                         h = oh * (w/ow);
31642                         h = Math.min(Math.max(mh, h), mxh);
31643                         w = ow * (h/oh);
31644                         y += th - h;
31645                         x += tw - w;
31646                        break;
31647
31648                 }
31649             }
31650             if (pos == 'hdrag') {
31651                 w = ow;
31652             }
31653             this.proxy.setBounds(x, y, w, h);
31654             if(this.dynamic){
31655                 this.resizeElement();
31656             }
31657             }catch(e){}
31658         }
31659         this.fireEvent("resizing", this, x, y, w, h, e);
31660     },
31661
31662     // private
31663     handleOver : function(){
31664         if(this.enabled){
31665             this.el.addClass("x-resizable-over");
31666         }
31667     },
31668
31669     // private
31670     handleOut : function(){
31671         if(!this.resizing){
31672             this.el.removeClass("x-resizable-over");
31673         }
31674     },
31675
31676     /**
31677      * Returns the element this component is bound to.
31678      * @return {Roo.Element}
31679      */
31680     getEl : function(){
31681         return this.el;
31682     },
31683
31684     /**
31685      * Returns the resizeChild element (or null).
31686      * @return {Roo.Element}
31687      */
31688     getResizeChild : function(){
31689         return this.resizeChild;
31690     },
31691     groupHandler : function()
31692     {
31693         
31694     },
31695     /**
31696      * Destroys this resizable. If the element was wrapped and
31697      * removeEl is not true then the element remains.
31698      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31699      */
31700     destroy : function(removeEl){
31701         this.proxy.remove();
31702         if(this.overlay){
31703             this.overlay.removeAllListeners();
31704             this.overlay.remove();
31705         }
31706         var ps = Roo.Resizable.positions;
31707         for(var k in ps){
31708             if(typeof ps[k] != "function" && this[ps[k]]){
31709                 var h = this[ps[k]];
31710                 h.el.removeAllListeners();
31711                 h.el.remove();
31712             }
31713         }
31714         if(removeEl){
31715             this.el.update("");
31716             this.el.remove();
31717         }
31718     }
31719 });
31720
31721 // private
31722 // hash to map config positions to true positions
31723 Roo.Resizable.positions = {
31724     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31725     hd: "hdrag"
31726 };
31727
31728 // private
31729 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31730     if(!this.tpl){
31731         // only initialize the template if resizable is used
31732         var tpl = Roo.DomHelper.createTemplate(
31733             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31734         );
31735         tpl.compile();
31736         Roo.Resizable.Handle.prototype.tpl = tpl;
31737     }
31738     this.position = pos;
31739     this.rz = rz;
31740     // show north drag fro topdra
31741     var handlepos = pos == 'hdrag' ? 'north' : pos;
31742     
31743     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31744     if (pos == 'hdrag') {
31745         this.el.setStyle('cursor', 'pointer');
31746     }
31747     this.el.unselectable();
31748     if(transparent){
31749         this.el.setOpacity(0);
31750     }
31751     this.el.on("mousedown", this.onMouseDown, this);
31752     if(!disableTrackOver){
31753         this.el.on("mouseover", this.onMouseOver, this);
31754         this.el.on("mouseout", this.onMouseOut, this);
31755     }
31756 };
31757
31758 // private
31759 Roo.Resizable.Handle.prototype = {
31760     afterResize : function(rz){
31761         Roo.log('after?');
31762         // do nothing
31763     },
31764     // private
31765     onMouseDown : function(e){
31766         this.rz.onMouseDown(this, e);
31767     },
31768     // private
31769     onMouseOver : function(e){
31770         this.rz.handleOver(this, e);
31771     },
31772     // private
31773     onMouseOut : function(e){
31774         this.rz.handleOut(this, e);
31775     }
31776 };/*
31777  * Based on:
31778  * Ext JS Library 1.1.1
31779  * Copyright(c) 2006-2007, Ext JS, LLC.
31780  *
31781  * Originally Released Under LGPL - original licence link has changed is not relivant.
31782  *
31783  * Fork - LGPL
31784  * <script type="text/javascript">
31785  */
31786
31787 /**
31788  * @class Roo.Editor
31789  * @extends Roo.Component
31790  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31791  * @constructor
31792  * Create a new Editor
31793  * @param {Roo.form.Field} field The Field object (or descendant)
31794  * @param {Object} config The config object
31795  */
31796 Roo.Editor = function(field, config){
31797     Roo.Editor.superclass.constructor.call(this, config);
31798     this.field = field;
31799     this.addEvents({
31800         /**
31801              * @event beforestartedit
31802              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31803              * false from the handler of this event.
31804              * @param {Editor} this
31805              * @param {Roo.Element} boundEl The underlying element bound to this editor
31806              * @param {Mixed} value The field value being set
31807              */
31808         "beforestartedit" : true,
31809         /**
31810              * @event startedit
31811              * Fires when this editor is displayed
31812              * @param {Roo.Element} boundEl The underlying element bound to this editor
31813              * @param {Mixed} value The starting field value
31814              */
31815         "startedit" : true,
31816         /**
31817              * @event beforecomplete
31818              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31819              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31820              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31821              * event will not fire since no edit actually occurred.
31822              * @param {Editor} this
31823              * @param {Mixed} value The current field value
31824              * @param {Mixed} startValue The original field value
31825              */
31826         "beforecomplete" : true,
31827         /**
31828              * @event complete
31829              * Fires after editing is complete and any changed value has been written to the underlying field.
31830              * @param {Editor} this
31831              * @param {Mixed} value The current field value
31832              * @param {Mixed} startValue The original field value
31833              */
31834         "complete" : true,
31835         /**
31836          * @event specialkey
31837          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31838          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31839          * @param {Roo.form.Field} this
31840          * @param {Roo.EventObject} e The event object
31841          */
31842         "specialkey" : true
31843     });
31844 };
31845
31846 Roo.extend(Roo.Editor, Roo.Component, {
31847     /**
31848      * @cfg {Boolean/String} autosize
31849      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31850      * or "height" to adopt the height only (defaults to false)
31851      */
31852     /**
31853      * @cfg {Boolean} revertInvalid
31854      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31855      * validation fails (defaults to true)
31856      */
31857     /**
31858      * @cfg {Boolean} ignoreNoChange
31859      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31860      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31861      * will never be ignored.
31862      */
31863     /**
31864      * @cfg {Boolean} hideEl
31865      * False to keep the bound element visible while the editor is displayed (defaults to true)
31866      */
31867     /**
31868      * @cfg {Mixed} value
31869      * The data value of the underlying field (defaults to "")
31870      */
31871     value : "",
31872     /**
31873      * @cfg {String} alignment
31874      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31875      */
31876     alignment: "c-c?",
31877     /**
31878      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31879      * for bottom-right shadow (defaults to "frame")
31880      */
31881     shadow : "frame",
31882     /**
31883      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31884      */
31885     constrain : false,
31886     /**
31887      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31888      */
31889     completeOnEnter : false,
31890     /**
31891      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31892      */
31893     cancelOnEsc : false,
31894     /**
31895      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31896      */
31897     updateEl : false,
31898
31899     // private
31900     onRender : function(ct, position){
31901         this.el = new Roo.Layer({
31902             shadow: this.shadow,
31903             cls: "x-editor",
31904             parentEl : ct,
31905             shim : this.shim,
31906             shadowOffset:4,
31907             id: this.id,
31908             constrain: this.constrain
31909         });
31910         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31911         if(this.field.msgTarget != 'title'){
31912             this.field.msgTarget = 'qtip';
31913         }
31914         this.field.render(this.el);
31915         if(Roo.isGecko){
31916             this.field.el.dom.setAttribute('autocomplete', 'off');
31917         }
31918         this.field.on("specialkey", this.onSpecialKey, this);
31919         if(this.swallowKeys){
31920             this.field.el.swallowEvent(['keydown','keypress']);
31921         }
31922         this.field.show();
31923         this.field.on("blur", this.onBlur, this);
31924         if(this.field.grow){
31925             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31926         }
31927     },
31928
31929     onSpecialKey : function(field, e)
31930     {
31931         //Roo.log('editor onSpecialKey');
31932         if(this.completeOnEnter && e.getKey() == e.ENTER){
31933             e.stopEvent();
31934             this.completeEdit();
31935             return;
31936         }
31937         // do not fire special key otherwise it might hide close the editor...
31938         if(e.getKey() == e.ENTER){    
31939             return;
31940         }
31941         if(this.cancelOnEsc && e.getKey() == e.ESC){
31942             this.cancelEdit();
31943             return;
31944         } 
31945         this.fireEvent('specialkey', field, e);
31946     
31947     },
31948
31949     /**
31950      * Starts the editing process and shows the editor.
31951      * @param {String/HTMLElement/Element} el The element to edit
31952      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31953       * to the innerHTML of el.
31954      */
31955     startEdit : function(el, value){
31956         if(this.editing){
31957             this.completeEdit();
31958         }
31959         this.boundEl = Roo.get(el);
31960         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31961         if(!this.rendered){
31962             this.render(this.parentEl || document.body);
31963         }
31964         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31965             return;
31966         }
31967         this.startValue = v;
31968         this.field.setValue(v);
31969         if(this.autoSize){
31970             var sz = this.boundEl.getSize();
31971             switch(this.autoSize){
31972                 case "width":
31973                 this.setSize(sz.width,  "");
31974                 break;
31975                 case "height":
31976                 this.setSize("",  sz.height);
31977                 break;
31978                 default:
31979                 this.setSize(sz.width,  sz.height);
31980             }
31981         }
31982         this.el.alignTo(this.boundEl, this.alignment);
31983         this.editing = true;
31984         if(Roo.QuickTips){
31985             Roo.QuickTips.disable();
31986         }
31987         this.show();
31988     },
31989
31990     /**
31991      * Sets the height and width of this editor.
31992      * @param {Number} width The new width
31993      * @param {Number} height The new height
31994      */
31995     setSize : function(w, h){
31996         this.field.setSize(w, h);
31997         if(this.el){
31998             this.el.sync();
31999         }
32000     },
32001
32002     /**
32003      * Realigns the editor to the bound field based on the current alignment config value.
32004      */
32005     realign : function(){
32006         this.el.alignTo(this.boundEl, this.alignment);
32007     },
32008
32009     /**
32010      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32011      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32012      */
32013     completeEdit : function(remainVisible){
32014         if(!this.editing){
32015             return;
32016         }
32017         var v = this.getValue();
32018         if(this.revertInvalid !== false && !this.field.isValid()){
32019             v = this.startValue;
32020             this.cancelEdit(true);
32021         }
32022         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32023             this.editing = false;
32024             this.hide();
32025             return;
32026         }
32027         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32028             this.editing = false;
32029             if(this.updateEl && this.boundEl){
32030                 this.boundEl.update(v);
32031             }
32032             if(remainVisible !== true){
32033                 this.hide();
32034             }
32035             this.fireEvent("complete", this, v, this.startValue);
32036         }
32037     },
32038
32039     // private
32040     onShow : function(){
32041         this.el.show();
32042         if(this.hideEl !== false){
32043             this.boundEl.hide();
32044         }
32045         this.field.show();
32046         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32047             this.fixIEFocus = true;
32048             this.deferredFocus.defer(50, this);
32049         }else{
32050             this.field.focus();
32051         }
32052         this.fireEvent("startedit", this.boundEl, this.startValue);
32053     },
32054
32055     deferredFocus : function(){
32056         if(this.editing){
32057             this.field.focus();
32058         }
32059     },
32060
32061     /**
32062      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32063      * reverted to the original starting value.
32064      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32065      * cancel (defaults to false)
32066      */
32067     cancelEdit : function(remainVisible){
32068         if(this.editing){
32069             this.setValue(this.startValue);
32070             if(remainVisible !== true){
32071                 this.hide();
32072             }
32073         }
32074     },
32075
32076     // private
32077     onBlur : function(){
32078         if(this.allowBlur !== true && this.editing){
32079             this.completeEdit();
32080         }
32081     },
32082
32083     // private
32084     onHide : function(){
32085         if(this.editing){
32086             this.completeEdit();
32087             return;
32088         }
32089         this.field.blur();
32090         if(this.field.collapse){
32091             this.field.collapse();
32092         }
32093         this.el.hide();
32094         if(this.hideEl !== false){
32095             this.boundEl.show();
32096         }
32097         if(Roo.QuickTips){
32098             Roo.QuickTips.enable();
32099         }
32100     },
32101
32102     /**
32103      * Sets the data value of the editor
32104      * @param {Mixed} value Any valid value supported by the underlying field
32105      */
32106     setValue : function(v){
32107         this.field.setValue(v);
32108     },
32109
32110     /**
32111      * Gets the data value of the editor
32112      * @return {Mixed} The data value
32113      */
32114     getValue : function(){
32115         return this.field.getValue();
32116     }
32117 });/*
32118  * Based on:
32119  * Ext JS Library 1.1.1
32120  * Copyright(c) 2006-2007, Ext JS, LLC.
32121  *
32122  * Originally Released Under LGPL - original licence link has changed is not relivant.
32123  *
32124  * Fork - LGPL
32125  * <script type="text/javascript">
32126  */
32127  
32128 /**
32129  * @class Roo.BasicDialog
32130  * @extends Roo.util.Observable
32131  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32132  * <pre><code>
32133 var dlg = new Roo.BasicDialog("my-dlg", {
32134     height: 200,
32135     width: 300,
32136     minHeight: 100,
32137     minWidth: 150,
32138     modal: true,
32139     proxyDrag: true,
32140     shadow: true
32141 });
32142 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32143 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32144 dlg.addButton('Cancel', dlg.hide, dlg);
32145 dlg.show();
32146 </code></pre>
32147   <b>A Dialog should always be a direct child of the body element.</b>
32148  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32149  * @cfg {String} title Default text to display in the title bar (defaults to null)
32150  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32151  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32152  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32153  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32154  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32155  * (defaults to null with no animation)
32156  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32157  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32158  * property for valid values (defaults to 'all')
32159  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32160  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32161  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32162  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32163  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32164  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32165  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32166  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32167  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32168  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32169  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32170  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32171  * draggable = true (defaults to false)
32172  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32173  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32174  * shadow (defaults to false)
32175  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32176  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32177  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32178  * @cfg {Array} buttons Array of buttons
32179  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32180  * @constructor
32181  * Create a new BasicDialog.
32182  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32183  * @param {Object} config Configuration options
32184  */
32185 Roo.BasicDialog = function(el, config){
32186     this.el = Roo.get(el);
32187     var dh = Roo.DomHelper;
32188     if(!this.el && config && config.autoCreate){
32189         if(typeof config.autoCreate == "object"){
32190             if(!config.autoCreate.id){
32191                 config.autoCreate.id = el;
32192             }
32193             this.el = dh.append(document.body,
32194                         config.autoCreate, true);
32195         }else{
32196             this.el = dh.append(document.body,
32197                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32198         }
32199     }
32200     el = this.el;
32201     el.setDisplayed(true);
32202     el.hide = this.hideAction;
32203     this.id = el.id;
32204     el.addClass("x-dlg");
32205
32206     Roo.apply(this, config);
32207
32208     this.proxy = el.createProxy("x-dlg-proxy");
32209     this.proxy.hide = this.hideAction;
32210     this.proxy.setOpacity(.5);
32211     this.proxy.hide();
32212
32213     if(config.width){
32214         el.setWidth(config.width);
32215     }
32216     if(config.height){
32217         el.setHeight(config.height);
32218     }
32219     this.size = el.getSize();
32220     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32221         this.xy = [config.x,config.y];
32222     }else{
32223         this.xy = el.getCenterXY(true);
32224     }
32225     /** The header element @type Roo.Element */
32226     this.header = el.child("> .x-dlg-hd");
32227     /** The body element @type Roo.Element */
32228     this.body = el.child("> .x-dlg-bd");
32229     /** The footer element @type Roo.Element */
32230     this.footer = el.child("> .x-dlg-ft");
32231
32232     if(!this.header){
32233         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32234     }
32235     if(!this.body){
32236         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32237     }
32238
32239     this.header.unselectable();
32240     if(this.title){
32241         this.header.update(this.title);
32242     }
32243     // this element allows the dialog to be focused for keyboard event
32244     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32245     this.focusEl.swallowEvent("click", true);
32246
32247     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32248
32249     // wrap the body and footer for special rendering
32250     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32251     if(this.footer){
32252         this.bwrap.dom.appendChild(this.footer.dom);
32253     }
32254
32255     this.bg = this.el.createChild({
32256         tag: "div", cls:"x-dlg-bg",
32257         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32258     });
32259     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32260
32261
32262     if(this.autoScroll !== false && !this.autoTabs){
32263         this.body.setStyle("overflow", "auto");
32264     }
32265
32266     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32267
32268     if(this.closable !== false){
32269         this.el.addClass("x-dlg-closable");
32270         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32271         this.close.on("click", this.closeClick, this);
32272         this.close.addClassOnOver("x-dlg-close-over");
32273     }
32274     if(this.collapsible !== false){
32275         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32276         this.collapseBtn.on("click", this.collapseClick, this);
32277         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32278         this.header.on("dblclick", this.collapseClick, this);
32279     }
32280     if(this.resizable !== false){
32281         this.el.addClass("x-dlg-resizable");
32282         this.resizer = new Roo.Resizable(el, {
32283             minWidth: this.minWidth || 80,
32284             minHeight:this.minHeight || 80,
32285             handles: this.resizeHandles || "all",
32286             pinned: true
32287         });
32288         this.resizer.on("beforeresize", this.beforeResize, this);
32289         this.resizer.on("resize", this.onResize, this);
32290     }
32291     if(this.draggable !== false){
32292         el.addClass("x-dlg-draggable");
32293         if (!this.proxyDrag) {
32294             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32295         }
32296         else {
32297             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32298         }
32299         dd.setHandleElId(this.header.id);
32300         dd.endDrag = this.endMove.createDelegate(this);
32301         dd.startDrag = this.startMove.createDelegate(this);
32302         dd.onDrag = this.onDrag.createDelegate(this);
32303         dd.scroll = false;
32304         this.dd = dd;
32305     }
32306     if(this.modal){
32307         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32308         this.mask.enableDisplayMode("block");
32309         this.mask.hide();
32310         this.el.addClass("x-dlg-modal");
32311     }
32312     if(this.shadow){
32313         this.shadow = new Roo.Shadow({
32314             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32315             offset : this.shadowOffset
32316         });
32317     }else{
32318         this.shadowOffset = 0;
32319     }
32320     if(Roo.useShims && this.shim !== false){
32321         this.shim = this.el.createShim();
32322         this.shim.hide = this.hideAction;
32323         this.shim.hide();
32324     }else{
32325         this.shim = false;
32326     }
32327     if(this.autoTabs){
32328         this.initTabs();
32329     }
32330     if (this.buttons) { 
32331         var bts= this.buttons;
32332         this.buttons = [];
32333         Roo.each(bts, function(b) {
32334             this.addButton(b);
32335         }, this);
32336     }
32337     
32338     
32339     this.addEvents({
32340         /**
32341          * @event keydown
32342          * Fires when a key is pressed
32343          * @param {Roo.BasicDialog} this
32344          * @param {Roo.EventObject} e
32345          */
32346         "keydown" : true,
32347         /**
32348          * @event move
32349          * Fires when this dialog is moved by the user.
32350          * @param {Roo.BasicDialog} this
32351          * @param {Number} x The new page X
32352          * @param {Number} y The new page Y
32353          */
32354         "move" : true,
32355         /**
32356          * @event resize
32357          * Fires when this dialog is resized by the user.
32358          * @param {Roo.BasicDialog} this
32359          * @param {Number} width The new width
32360          * @param {Number} height The new height
32361          */
32362         "resize" : true,
32363         /**
32364          * @event beforehide
32365          * Fires before this dialog is hidden.
32366          * @param {Roo.BasicDialog} this
32367          */
32368         "beforehide" : true,
32369         /**
32370          * @event hide
32371          * Fires when this dialog is hidden.
32372          * @param {Roo.BasicDialog} this
32373          */
32374         "hide" : true,
32375         /**
32376          * @event beforeshow
32377          * Fires before this dialog is shown.
32378          * @param {Roo.BasicDialog} this
32379          */
32380         "beforeshow" : true,
32381         /**
32382          * @event show
32383          * Fires when this dialog is shown.
32384          * @param {Roo.BasicDialog} this
32385          */
32386         "show" : true
32387     });
32388     el.on("keydown", this.onKeyDown, this);
32389     el.on("mousedown", this.toFront, this);
32390     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32391     this.el.hide();
32392     Roo.DialogManager.register(this);
32393     Roo.BasicDialog.superclass.constructor.call(this);
32394 };
32395
32396 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32397     shadowOffset: Roo.isIE ? 6 : 5,
32398     minHeight: 80,
32399     minWidth: 200,
32400     minButtonWidth: 75,
32401     defaultButton: null,
32402     buttonAlign: "right",
32403     tabTag: 'div',
32404     firstShow: true,
32405
32406     /**
32407      * Sets the dialog title text
32408      * @param {String} text The title text to display
32409      * @return {Roo.BasicDialog} this
32410      */
32411     setTitle : function(text){
32412         this.header.update(text);
32413         return this;
32414     },
32415
32416     // private
32417     closeClick : function(){
32418         this.hide();
32419     },
32420
32421     // private
32422     collapseClick : function(){
32423         this[this.collapsed ? "expand" : "collapse"]();
32424     },
32425
32426     /**
32427      * Collapses the dialog to its minimized state (only the title bar is visible).
32428      * Equivalent to the user clicking the collapse dialog button.
32429      */
32430     collapse : function(){
32431         if(!this.collapsed){
32432             this.collapsed = true;
32433             this.el.addClass("x-dlg-collapsed");
32434             this.restoreHeight = this.el.getHeight();
32435             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32436         }
32437     },
32438
32439     /**
32440      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32441      * clicking the expand dialog button.
32442      */
32443     expand : function(){
32444         if(this.collapsed){
32445             this.collapsed = false;
32446             this.el.removeClass("x-dlg-collapsed");
32447             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32448         }
32449     },
32450
32451     /**
32452      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32453      * @return {Roo.TabPanel} The tabs component
32454      */
32455     initTabs : function(){
32456         var tabs = this.getTabs();
32457         while(tabs.getTab(0)){
32458             tabs.removeTab(0);
32459         }
32460         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32461             var dom = el.dom;
32462             tabs.addTab(Roo.id(dom), dom.title);
32463             dom.title = "";
32464         });
32465         tabs.activate(0);
32466         return tabs;
32467     },
32468
32469     // private
32470     beforeResize : function(){
32471         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32472     },
32473
32474     // private
32475     onResize : function(){
32476         this.refreshSize();
32477         this.syncBodyHeight();
32478         this.adjustAssets();
32479         this.focus();
32480         this.fireEvent("resize", this, this.size.width, this.size.height);
32481     },
32482
32483     // private
32484     onKeyDown : function(e){
32485         if(this.isVisible()){
32486             this.fireEvent("keydown", this, e);
32487         }
32488     },
32489
32490     /**
32491      * Resizes the dialog.
32492      * @param {Number} width
32493      * @param {Number} height
32494      * @return {Roo.BasicDialog} this
32495      */
32496     resizeTo : function(width, height){
32497         this.el.setSize(width, height);
32498         this.size = {width: width, height: height};
32499         this.syncBodyHeight();
32500         if(this.fixedcenter){
32501             this.center();
32502         }
32503         if(this.isVisible()){
32504             this.constrainXY();
32505             this.adjustAssets();
32506         }
32507         this.fireEvent("resize", this, width, height);
32508         return this;
32509     },
32510
32511
32512     /**
32513      * Resizes the dialog to fit the specified content size.
32514      * @param {Number} width
32515      * @param {Number} height
32516      * @return {Roo.BasicDialog} this
32517      */
32518     setContentSize : function(w, h){
32519         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32520         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32521         //if(!this.el.isBorderBox()){
32522             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32523             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32524         //}
32525         if(this.tabs){
32526             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32527             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32528         }
32529         this.resizeTo(w, h);
32530         return this;
32531     },
32532
32533     /**
32534      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32535      * executed in response to a particular key being pressed while the dialog is active.
32536      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32537      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32538      * @param {Function} fn The function to call
32539      * @param {Object} scope (optional) The scope of the function
32540      * @return {Roo.BasicDialog} this
32541      */
32542     addKeyListener : function(key, fn, scope){
32543         var keyCode, shift, ctrl, alt;
32544         if(typeof key == "object" && !(key instanceof Array)){
32545             keyCode = key["key"];
32546             shift = key["shift"];
32547             ctrl = key["ctrl"];
32548             alt = key["alt"];
32549         }else{
32550             keyCode = key;
32551         }
32552         var handler = function(dlg, e){
32553             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32554                 var k = e.getKey();
32555                 if(keyCode instanceof Array){
32556                     for(var i = 0, len = keyCode.length; i < len; i++){
32557                         if(keyCode[i] == k){
32558                           fn.call(scope || window, dlg, k, e);
32559                           return;
32560                         }
32561                     }
32562                 }else{
32563                     if(k == keyCode){
32564                         fn.call(scope || window, dlg, k, e);
32565                     }
32566                 }
32567             }
32568         };
32569         this.on("keydown", handler);
32570         return this;
32571     },
32572
32573     /**
32574      * Returns the TabPanel component (creates it if it doesn't exist).
32575      * Note: If you wish to simply check for the existence of tabs without creating them,
32576      * check for a null 'tabs' property.
32577      * @return {Roo.TabPanel} The tabs component
32578      */
32579     getTabs : function(){
32580         if(!this.tabs){
32581             this.el.addClass("x-dlg-auto-tabs");
32582             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32583             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32584         }
32585         return this.tabs;
32586     },
32587
32588     /**
32589      * Adds a button to the footer section of the dialog.
32590      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32591      * object or a valid Roo.DomHelper element config
32592      * @param {Function} handler The function called when the button is clicked
32593      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32594      * @return {Roo.Button} The new button
32595      */
32596     addButton : function(config, handler, scope){
32597         var dh = Roo.DomHelper;
32598         if(!this.footer){
32599             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32600         }
32601         if(!this.btnContainer){
32602             var tb = this.footer.createChild({
32603
32604                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32605                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32606             }, null, true);
32607             this.btnContainer = tb.firstChild.firstChild.firstChild;
32608         }
32609         var bconfig = {
32610             handler: handler,
32611             scope: scope,
32612             minWidth: this.minButtonWidth,
32613             hideParent:true
32614         };
32615         if(typeof config == "string"){
32616             bconfig.text = config;
32617         }else{
32618             if(config.tag){
32619                 bconfig.dhconfig = config;
32620             }else{
32621                 Roo.apply(bconfig, config);
32622             }
32623         }
32624         var fc = false;
32625         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32626             bconfig.position = Math.max(0, bconfig.position);
32627             fc = this.btnContainer.childNodes[bconfig.position];
32628         }
32629          
32630         var btn = new Roo.Button(
32631             fc ? 
32632                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32633                 : this.btnContainer.appendChild(document.createElement("td")),
32634             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32635             bconfig
32636         );
32637         this.syncBodyHeight();
32638         if(!this.buttons){
32639             /**
32640              * Array of all the buttons that have been added to this dialog via addButton
32641              * @type Array
32642              */
32643             this.buttons = [];
32644         }
32645         this.buttons.push(btn);
32646         return btn;
32647     },
32648
32649     /**
32650      * Sets the default button to be focused when the dialog is displayed.
32651      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32652      * @return {Roo.BasicDialog} this
32653      */
32654     setDefaultButton : function(btn){
32655         this.defaultButton = btn;
32656         return this;
32657     },
32658
32659     // private
32660     getHeaderFooterHeight : function(safe){
32661         var height = 0;
32662         if(this.header){
32663            height += this.header.getHeight();
32664         }
32665         if(this.footer){
32666            var fm = this.footer.getMargins();
32667             height += (this.footer.getHeight()+fm.top+fm.bottom);
32668         }
32669         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32670         height += this.centerBg.getPadding("tb");
32671         return height;
32672     },
32673
32674     // private
32675     syncBodyHeight : function()
32676     {
32677         var bd = this.body, // the text
32678             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32679             bw = this.bwrap;
32680         var height = this.size.height - this.getHeaderFooterHeight(false);
32681         bd.setHeight(height-bd.getMargins("tb"));
32682         var hh = this.header.getHeight();
32683         var h = this.size.height-hh;
32684         cb.setHeight(h);
32685         
32686         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32687         bw.setHeight(h-cb.getPadding("tb"));
32688         
32689         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32690         bd.setWidth(bw.getWidth(true));
32691         if(this.tabs){
32692             this.tabs.syncHeight();
32693             if(Roo.isIE){
32694                 this.tabs.el.repaint();
32695             }
32696         }
32697     },
32698
32699     /**
32700      * Restores the previous state of the dialog if Roo.state is configured.
32701      * @return {Roo.BasicDialog} this
32702      */
32703     restoreState : function(){
32704         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32705         if(box && box.width){
32706             this.xy = [box.x, box.y];
32707             this.resizeTo(box.width, box.height);
32708         }
32709         return this;
32710     },
32711
32712     // private
32713     beforeShow : function(){
32714         this.expand();
32715         if(this.fixedcenter){
32716             this.xy = this.el.getCenterXY(true);
32717         }
32718         if(this.modal){
32719             Roo.get(document.body).addClass("x-body-masked");
32720             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32721             this.mask.show();
32722         }
32723         this.constrainXY();
32724     },
32725
32726     // private
32727     animShow : function(){
32728         var b = Roo.get(this.animateTarget).getBox();
32729         this.proxy.setSize(b.width, b.height);
32730         this.proxy.setLocation(b.x, b.y);
32731         this.proxy.show();
32732         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32733                     true, .35, this.showEl.createDelegate(this));
32734     },
32735
32736     /**
32737      * Shows the dialog.
32738      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32739      * @return {Roo.BasicDialog} this
32740      */
32741     show : function(animateTarget){
32742         if (this.fireEvent("beforeshow", this) === false){
32743             return;
32744         }
32745         if(this.syncHeightBeforeShow){
32746             this.syncBodyHeight();
32747         }else if(this.firstShow){
32748             this.firstShow = false;
32749             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32750         }
32751         this.animateTarget = animateTarget || this.animateTarget;
32752         if(!this.el.isVisible()){
32753             this.beforeShow();
32754             if(this.animateTarget && Roo.get(this.animateTarget)){
32755                 this.animShow();
32756             }else{
32757                 this.showEl();
32758             }
32759         }
32760         return this;
32761     },
32762
32763     // private
32764     showEl : function(){
32765         this.proxy.hide();
32766         this.el.setXY(this.xy);
32767         this.el.show();
32768         this.adjustAssets(true);
32769         this.toFront();
32770         this.focus();
32771         // IE peekaboo bug - fix found by Dave Fenwick
32772         if(Roo.isIE){
32773             this.el.repaint();
32774         }
32775         this.fireEvent("show", this);
32776     },
32777
32778     /**
32779      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32780      * dialog itself will receive focus.
32781      */
32782     focus : function(){
32783         if(this.defaultButton){
32784             this.defaultButton.focus();
32785         }else{
32786             this.focusEl.focus();
32787         }
32788     },
32789
32790     // private
32791     constrainXY : function(){
32792         if(this.constraintoviewport !== false){
32793             if(!this.viewSize){
32794                 if(this.container){
32795                     var s = this.container.getSize();
32796                     this.viewSize = [s.width, s.height];
32797                 }else{
32798                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32799                 }
32800             }
32801             var s = Roo.get(this.container||document).getScroll();
32802
32803             var x = this.xy[0], y = this.xy[1];
32804             var w = this.size.width, h = this.size.height;
32805             var vw = this.viewSize[0], vh = this.viewSize[1];
32806             // only move it if it needs it
32807             var moved = false;
32808             // first validate right/bottom
32809             if(x + w > vw+s.left){
32810                 x = vw - w;
32811                 moved = true;
32812             }
32813             if(y + h > vh+s.top){
32814                 y = vh - h;
32815                 moved = true;
32816             }
32817             // then make sure top/left isn't negative
32818             if(x < s.left){
32819                 x = s.left;
32820                 moved = true;
32821             }
32822             if(y < s.top){
32823                 y = s.top;
32824                 moved = true;
32825             }
32826             if(moved){
32827                 // cache xy
32828                 this.xy = [x, y];
32829                 if(this.isVisible()){
32830                     this.el.setLocation(x, y);
32831                     this.adjustAssets();
32832                 }
32833             }
32834         }
32835     },
32836
32837     // private
32838     onDrag : function(){
32839         if(!this.proxyDrag){
32840             this.xy = this.el.getXY();
32841             this.adjustAssets();
32842         }
32843     },
32844
32845     // private
32846     adjustAssets : function(doShow){
32847         var x = this.xy[0], y = this.xy[1];
32848         var w = this.size.width, h = this.size.height;
32849         if(doShow === true){
32850             if(this.shadow){
32851                 this.shadow.show(this.el);
32852             }
32853             if(this.shim){
32854                 this.shim.show();
32855             }
32856         }
32857         if(this.shadow && this.shadow.isVisible()){
32858             this.shadow.show(this.el);
32859         }
32860         if(this.shim && this.shim.isVisible()){
32861             this.shim.setBounds(x, y, w, h);
32862         }
32863     },
32864
32865     // private
32866     adjustViewport : function(w, h){
32867         if(!w || !h){
32868             w = Roo.lib.Dom.getViewWidth();
32869             h = Roo.lib.Dom.getViewHeight();
32870         }
32871         // cache the size
32872         this.viewSize = [w, h];
32873         if(this.modal && this.mask.isVisible()){
32874             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32875             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32876         }
32877         if(this.isVisible()){
32878             this.constrainXY();
32879         }
32880     },
32881
32882     /**
32883      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32884      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32885      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32886      */
32887     destroy : function(removeEl){
32888         if(this.isVisible()){
32889             this.animateTarget = null;
32890             this.hide();
32891         }
32892         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32893         if(this.tabs){
32894             this.tabs.destroy(removeEl);
32895         }
32896         Roo.destroy(
32897              this.shim,
32898              this.proxy,
32899              this.resizer,
32900              this.close,
32901              this.mask
32902         );
32903         if(this.dd){
32904             this.dd.unreg();
32905         }
32906         if(this.buttons){
32907            for(var i = 0, len = this.buttons.length; i < len; i++){
32908                this.buttons[i].destroy();
32909            }
32910         }
32911         this.el.removeAllListeners();
32912         if(removeEl === true){
32913             this.el.update("");
32914             this.el.remove();
32915         }
32916         Roo.DialogManager.unregister(this);
32917     },
32918
32919     // private
32920     startMove : function(){
32921         if(this.proxyDrag){
32922             this.proxy.show();
32923         }
32924         if(this.constraintoviewport !== false){
32925             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32926         }
32927     },
32928
32929     // private
32930     endMove : function(){
32931         if(!this.proxyDrag){
32932             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32933         }else{
32934             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32935             this.proxy.hide();
32936         }
32937         this.refreshSize();
32938         this.adjustAssets();
32939         this.focus();
32940         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32941     },
32942
32943     /**
32944      * Brings this dialog to the front of any other visible dialogs
32945      * @return {Roo.BasicDialog} this
32946      */
32947     toFront : function(){
32948         Roo.DialogManager.bringToFront(this);
32949         return this;
32950     },
32951
32952     /**
32953      * Sends this dialog to the back (under) of any other visible dialogs
32954      * @return {Roo.BasicDialog} this
32955      */
32956     toBack : function(){
32957         Roo.DialogManager.sendToBack(this);
32958         return this;
32959     },
32960
32961     /**
32962      * Centers this dialog in the viewport
32963      * @return {Roo.BasicDialog} this
32964      */
32965     center : function(){
32966         var xy = this.el.getCenterXY(true);
32967         this.moveTo(xy[0], xy[1]);
32968         return this;
32969     },
32970
32971     /**
32972      * Moves the dialog's top-left corner to the specified point
32973      * @param {Number} x
32974      * @param {Number} y
32975      * @return {Roo.BasicDialog} this
32976      */
32977     moveTo : function(x, y){
32978         this.xy = [x,y];
32979         if(this.isVisible()){
32980             this.el.setXY(this.xy);
32981             this.adjustAssets();
32982         }
32983         return this;
32984     },
32985
32986     /**
32987      * Aligns the dialog to the specified element
32988      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32989      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32990      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32991      * @return {Roo.BasicDialog} this
32992      */
32993     alignTo : function(element, position, offsets){
32994         this.xy = this.el.getAlignToXY(element, position, offsets);
32995         if(this.isVisible()){
32996             this.el.setXY(this.xy);
32997             this.adjustAssets();
32998         }
32999         return this;
33000     },
33001
33002     /**
33003      * Anchors an element to another element and realigns it when the window is resized.
33004      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33005      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33006      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33007      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33008      * is a number, it is used as the buffer delay (defaults to 50ms).
33009      * @return {Roo.BasicDialog} this
33010      */
33011     anchorTo : function(el, alignment, offsets, monitorScroll){
33012         var action = function(){
33013             this.alignTo(el, alignment, offsets);
33014         };
33015         Roo.EventManager.onWindowResize(action, this);
33016         var tm = typeof monitorScroll;
33017         if(tm != 'undefined'){
33018             Roo.EventManager.on(window, 'scroll', action, this,
33019                 {buffer: tm == 'number' ? monitorScroll : 50});
33020         }
33021         action.call(this);
33022         return this;
33023     },
33024
33025     /**
33026      * Returns true if the dialog is visible
33027      * @return {Boolean}
33028      */
33029     isVisible : function(){
33030         return this.el.isVisible();
33031     },
33032
33033     // private
33034     animHide : function(callback){
33035         var b = Roo.get(this.animateTarget).getBox();
33036         this.proxy.show();
33037         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33038         this.el.hide();
33039         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33040                     this.hideEl.createDelegate(this, [callback]));
33041     },
33042
33043     /**
33044      * Hides the dialog.
33045      * @param {Function} callback (optional) Function to call when the dialog is hidden
33046      * @return {Roo.BasicDialog} this
33047      */
33048     hide : function(callback){
33049         if (this.fireEvent("beforehide", this) === false){
33050             return;
33051         }
33052         if(this.shadow){
33053             this.shadow.hide();
33054         }
33055         if(this.shim) {
33056           this.shim.hide();
33057         }
33058         // sometimes animateTarget seems to get set.. causing problems...
33059         // this just double checks..
33060         if(this.animateTarget && Roo.get(this.animateTarget)) {
33061            this.animHide(callback);
33062         }else{
33063             this.el.hide();
33064             this.hideEl(callback);
33065         }
33066         return this;
33067     },
33068
33069     // private
33070     hideEl : function(callback){
33071         this.proxy.hide();
33072         if(this.modal){
33073             this.mask.hide();
33074             Roo.get(document.body).removeClass("x-body-masked");
33075         }
33076         this.fireEvent("hide", this);
33077         if(typeof callback == "function"){
33078             callback();
33079         }
33080     },
33081
33082     // private
33083     hideAction : function(){
33084         this.setLeft("-10000px");
33085         this.setTop("-10000px");
33086         this.setStyle("visibility", "hidden");
33087     },
33088
33089     // private
33090     refreshSize : function(){
33091         this.size = this.el.getSize();
33092         this.xy = this.el.getXY();
33093         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33094     },
33095
33096     // private
33097     // z-index is managed by the DialogManager and may be overwritten at any time
33098     setZIndex : function(index){
33099         if(this.modal){
33100             this.mask.setStyle("z-index", index);
33101         }
33102         if(this.shim){
33103             this.shim.setStyle("z-index", ++index);
33104         }
33105         if(this.shadow){
33106             this.shadow.setZIndex(++index);
33107         }
33108         this.el.setStyle("z-index", ++index);
33109         if(this.proxy){
33110             this.proxy.setStyle("z-index", ++index);
33111         }
33112         if(this.resizer){
33113             this.resizer.proxy.setStyle("z-index", ++index);
33114         }
33115
33116         this.lastZIndex = index;
33117     },
33118
33119     /**
33120      * Returns the element for this dialog
33121      * @return {Roo.Element} The underlying dialog Element
33122      */
33123     getEl : function(){
33124         return this.el;
33125     }
33126 });
33127
33128 /**
33129  * @class Roo.DialogManager
33130  * Provides global access to BasicDialogs that have been created and
33131  * support for z-indexing (layering) multiple open dialogs.
33132  */
33133 Roo.DialogManager = function(){
33134     var list = {};
33135     var accessList = [];
33136     var front = null;
33137
33138     // private
33139     var sortDialogs = function(d1, d2){
33140         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33141     };
33142
33143     // private
33144     var orderDialogs = function(){
33145         accessList.sort(sortDialogs);
33146         var seed = Roo.DialogManager.zseed;
33147         for(var i = 0, len = accessList.length; i < len; i++){
33148             var dlg = accessList[i];
33149             if(dlg){
33150                 dlg.setZIndex(seed + (i*10));
33151             }
33152         }
33153     };
33154
33155     return {
33156         /**
33157          * The starting z-index for BasicDialogs (defaults to 9000)
33158          * @type Number The z-index value
33159          */
33160         zseed : 9000,
33161
33162         // private
33163         register : function(dlg){
33164             list[dlg.id] = dlg;
33165             accessList.push(dlg);
33166         },
33167
33168         // private
33169         unregister : function(dlg){
33170             delete list[dlg.id];
33171             var i=0;
33172             var len=0;
33173             if(!accessList.indexOf){
33174                 for(  i = 0, len = accessList.length; i < len; i++){
33175                     if(accessList[i] == dlg){
33176                         accessList.splice(i, 1);
33177                         return;
33178                     }
33179                 }
33180             }else{
33181                  i = accessList.indexOf(dlg);
33182                 if(i != -1){
33183                     accessList.splice(i, 1);
33184                 }
33185             }
33186         },
33187
33188         /**
33189          * Gets a registered dialog by id
33190          * @param {String/Object} id The id of the dialog or a dialog
33191          * @return {Roo.BasicDialog} this
33192          */
33193         get : function(id){
33194             return typeof id == "object" ? id : list[id];
33195         },
33196
33197         /**
33198          * Brings the specified dialog to the front
33199          * @param {String/Object} dlg The id of the dialog or a dialog
33200          * @return {Roo.BasicDialog} this
33201          */
33202         bringToFront : function(dlg){
33203             dlg = this.get(dlg);
33204             if(dlg != front){
33205                 front = dlg;
33206                 dlg._lastAccess = new Date().getTime();
33207                 orderDialogs();
33208             }
33209             return dlg;
33210         },
33211
33212         /**
33213          * Sends the specified dialog to the back
33214          * @param {String/Object} dlg The id of the dialog or a dialog
33215          * @return {Roo.BasicDialog} this
33216          */
33217         sendToBack : function(dlg){
33218             dlg = this.get(dlg);
33219             dlg._lastAccess = -(new Date().getTime());
33220             orderDialogs();
33221             return dlg;
33222         },
33223
33224         /**
33225          * Hides all dialogs
33226          */
33227         hideAll : function(){
33228             for(var id in list){
33229                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33230                     list[id].hide();
33231                 }
33232             }
33233         }
33234     };
33235 }();
33236
33237 /**
33238  * @class Roo.LayoutDialog
33239  * @extends Roo.BasicDialog
33240  * Dialog which provides adjustments for working with a layout in a Dialog.
33241  * Add your necessary layout config options to the dialog's config.<br>
33242  * Example usage (including a nested layout):
33243  * <pre><code>
33244 if(!dialog){
33245     dialog = new Roo.LayoutDialog("download-dlg", {
33246         modal: true,
33247         width:600,
33248         height:450,
33249         shadow:true,
33250         minWidth:500,
33251         minHeight:350,
33252         autoTabs:true,
33253         proxyDrag:true,
33254         // layout config merges with the dialog config
33255         center:{
33256             tabPosition: "top",
33257             alwaysShowTabs: true
33258         }
33259     });
33260     dialog.addKeyListener(27, dialog.hide, dialog);
33261     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33262     dialog.addButton("Build It!", this.getDownload, this);
33263
33264     // we can even add nested layouts
33265     var innerLayout = new Roo.BorderLayout("dl-inner", {
33266         east: {
33267             initialSize: 200,
33268             autoScroll:true,
33269             split:true
33270         },
33271         center: {
33272             autoScroll:true
33273         }
33274     });
33275     innerLayout.beginUpdate();
33276     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33277     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33278     innerLayout.endUpdate(true);
33279
33280     var layout = dialog.getLayout();
33281     layout.beginUpdate();
33282     layout.add("center", new Roo.ContentPanel("standard-panel",
33283                         {title: "Download the Source", fitToFrame:true}));
33284     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33285                {title: "Build your own roo.js"}));
33286     layout.getRegion("center").showPanel(sp);
33287     layout.endUpdate();
33288 }
33289 </code></pre>
33290     * @constructor
33291     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33292     * @param {Object} config configuration options
33293   */
33294 Roo.LayoutDialog = function(el, cfg){
33295     
33296     var config=  cfg;
33297     if (typeof(cfg) == 'undefined') {
33298         config = Roo.apply({}, el);
33299         // not sure why we use documentElement here.. - it should always be body.
33300         // IE7 borks horribly if we use documentElement.
33301         // webkit also does not like documentElement - it creates a body element...
33302         el = Roo.get( document.body || document.documentElement ).createChild();
33303         //config.autoCreate = true;
33304     }
33305     
33306     
33307     config.autoTabs = false;
33308     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33309     this.body.setStyle({overflow:"hidden", position:"relative"});
33310     this.layout = new Roo.BorderLayout(this.body.dom, config);
33311     this.layout.monitorWindowResize = false;
33312     this.el.addClass("x-dlg-auto-layout");
33313     // fix case when center region overwrites center function
33314     this.center = Roo.BasicDialog.prototype.center;
33315     this.on("show", this.layout.layout, this.layout, true);
33316     if (config.items) {
33317         var xitems = config.items;
33318         delete config.items;
33319         Roo.each(xitems, this.addxtype, this);
33320     }
33321     
33322     
33323 };
33324 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33325     /**
33326      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33327      * @deprecated
33328      */
33329     endUpdate : function(){
33330         this.layout.endUpdate();
33331     },
33332
33333     /**
33334      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33335      *  @deprecated
33336      */
33337     beginUpdate : function(){
33338         this.layout.beginUpdate();
33339     },
33340
33341     /**
33342      * Get the BorderLayout for this dialog
33343      * @return {Roo.BorderLayout}
33344      */
33345     getLayout : function(){
33346         return this.layout;
33347     },
33348
33349     showEl : function(){
33350         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33351         if(Roo.isIE7){
33352             this.layout.layout();
33353         }
33354     },
33355
33356     // private
33357     // Use the syncHeightBeforeShow config option to control this automatically
33358     syncBodyHeight : function(){
33359         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33360         if(this.layout){this.layout.layout();}
33361     },
33362     
33363       /**
33364      * Add an xtype element (actually adds to the layout.)
33365      * @return {Object} xdata xtype object data.
33366      */
33367     
33368     addxtype : function(c) {
33369         return this.layout.addxtype(c);
33370     }
33371 });/*
33372  * Based on:
33373  * Ext JS Library 1.1.1
33374  * Copyright(c) 2006-2007, Ext JS, LLC.
33375  *
33376  * Originally Released Under LGPL - original licence link has changed is not relivant.
33377  *
33378  * Fork - LGPL
33379  * <script type="text/javascript">
33380  */
33381  
33382 /**
33383  * @class Roo.MessageBox
33384  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33385  * Example usage:
33386  *<pre><code>
33387 // Basic alert:
33388 Roo.Msg.alert('Status', 'Changes saved successfully.');
33389
33390 // Prompt for user data:
33391 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33392     if (btn == 'ok'){
33393         // process text value...
33394     }
33395 });
33396
33397 // Show a dialog using config options:
33398 Roo.Msg.show({
33399    title:'Save Changes?',
33400    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33401    buttons: Roo.Msg.YESNOCANCEL,
33402    fn: processResult,
33403    animEl: 'elId'
33404 });
33405 </code></pre>
33406  * @singleton
33407  */
33408 Roo.MessageBox = function(){
33409     var dlg, opt, mask, waitTimer;
33410     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33411     var buttons, activeTextEl, bwidth;
33412
33413     // private
33414     var handleButton = function(button){
33415         dlg.hide();
33416         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33417     };
33418
33419     // private
33420     var handleHide = function(){
33421         if(opt && opt.cls){
33422             dlg.el.removeClass(opt.cls);
33423         }
33424         if(waitTimer){
33425             Roo.TaskMgr.stop(waitTimer);
33426             waitTimer = null;
33427         }
33428     };
33429
33430     // private
33431     var updateButtons = function(b){
33432         var width = 0;
33433         if(!b){
33434             buttons["ok"].hide();
33435             buttons["cancel"].hide();
33436             buttons["yes"].hide();
33437             buttons["no"].hide();
33438             dlg.footer.dom.style.display = 'none';
33439             return width;
33440         }
33441         dlg.footer.dom.style.display = '';
33442         for(var k in buttons){
33443             if(typeof buttons[k] != "function"){
33444                 if(b[k]){
33445                     buttons[k].show();
33446                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33447                     width += buttons[k].el.getWidth()+15;
33448                 }else{
33449                     buttons[k].hide();
33450                 }
33451             }
33452         }
33453         return width;
33454     };
33455
33456     // private
33457     var handleEsc = function(d, k, e){
33458         if(opt && opt.closable !== false){
33459             dlg.hide();
33460         }
33461         if(e){
33462             e.stopEvent();
33463         }
33464     };
33465
33466     return {
33467         /**
33468          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33469          * @return {Roo.BasicDialog} The BasicDialog element
33470          */
33471         getDialog : function(){
33472            if(!dlg){
33473                 dlg = new Roo.BasicDialog("x-msg-box", {
33474                     autoCreate : true,
33475                     shadow: true,
33476                     draggable: true,
33477                     resizable:false,
33478                     constraintoviewport:false,
33479                     fixedcenter:true,
33480                     collapsible : false,
33481                     shim:true,
33482                     modal: true,
33483                     width:400, height:100,
33484                     buttonAlign:"center",
33485                     closeClick : function(){
33486                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33487                             handleButton("no");
33488                         }else{
33489                             handleButton("cancel");
33490                         }
33491                     }
33492                 });
33493                 dlg.on("hide", handleHide);
33494                 mask = dlg.mask;
33495                 dlg.addKeyListener(27, handleEsc);
33496                 buttons = {};
33497                 var bt = this.buttonText;
33498                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33499                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33500                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33501                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33502                 bodyEl = dlg.body.createChild({
33503
33504                     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>'
33505                 });
33506                 msgEl = bodyEl.dom.firstChild;
33507                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33508                 textboxEl.enableDisplayMode();
33509                 textboxEl.addKeyListener([10,13], function(){
33510                     if(dlg.isVisible() && opt && opt.buttons){
33511                         if(opt.buttons.ok){
33512                             handleButton("ok");
33513                         }else if(opt.buttons.yes){
33514                             handleButton("yes");
33515                         }
33516                     }
33517                 });
33518                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33519                 textareaEl.enableDisplayMode();
33520                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33521                 progressEl.enableDisplayMode();
33522                 var pf = progressEl.dom.firstChild;
33523                 if (pf) {
33524                     pp = Roo.get(pf.firstChild);
33525                     pp.setHeight(pf.offsetHeight);
33526                 }
33527                 
33528             }
33529             return dlg;
33530         },
33531
33532         /**
33533          * Updates the message box body text
33534          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33535          * the XHTML-compliant non-breaking space character '&amp;#160;')
33536          * @return {Roo.MessageBox} This message box
33537          */
33538         updateText : function(text){
33539             if(!dlg.isVisible() && !opt.width){
33540                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33541             }
33542             msgEl.innerHTML = text || '&#160;';
33543       
33544             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33545             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33546             var w = Math.max(
33547                     Math.min(opt.width || cw , this.maxWidth), 
33548                     Math.max(opt.minWidth || this.minWidth, bwidth)
33549             );
33550             if(opt.prompt){
33551                 activeTextEl.setWidth(w);
33552             }
33553             if(dlg.isVisible()){
33554                 dlg.fixedcenter = false;
33555             }
33556             // to big, make it scroll. = But as usual stupid IE does not support
33557             // !important..
33558             
33559             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33560                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33561                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33562             } else {
33563                 bodyEl.dom.style.height = '';
33564                 bodyEl.dom.style.overflowY = '';
33565             }
33566             if (cw > w) {
33567                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33568             } else {
33569                 bodyEl.dom.style.overflowX = '';
33570             }
33571             
33572             dlg.setContentSize(w, bodyEl.getHeight());
33573             if(dlg.isVisible()){
33574                 dlg.fixedcenter = true;
33575             }
33576             return this;
33577         },
33578
33579         /**
33580          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33581          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33582          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33583          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33584          * @return {Roo.MessageBox} This message box
33585          */
33586         updateProgress : function(value, text){
33587             if(text){
33588                 this.updateText(text);
33589             }
33590             if (pp) { // weird bug on my firefox - for some reason this is not defined
33591                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33592             }
33593             return this;
33594         },        
33595
33596         /**
33597          * Returns true if the message box is currently displayed
33598          * @return {Boolean} True if the message box is visible, else false
33599          */
33600         isVisible : function(){
33601             return dlg && dlg.isVisible();  
33602         },
33603
33604         /**
33605          * Hides the message box if it is displayed
33606          */
33607         hide : function(){
33608             if(this.isVisible()){
33609                 dlg.hide();
33610             }  
33611         },
33612
33613         /**
33614          * Displays a new message box, or reinitializes an existing message box, based on the config options
33615          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33616          * The following config object properties are supported:
33617          * <pre>
33618 Property    Type             Description
33619 ----------  ---------------  ------------------------------------------------------------------------------------
33620 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33621                                    closes (defaults to undefined)
33622 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33623                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33624 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33625                                    progress and wait dialogs will ignore this property and always hide the
33626                                    close button as they can only be closed programmatically.
33627 cls               String           A custom CSS class to apply to the message box element
33628 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33629                                    displayed (defaults to 75)
33630 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33631                                    function will be btn (the name of the button that was clicked, if applicable,
33632                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33633                                    Progress and wait dialogs will ignore this option since they do not respond to
33634                                    user actions and can only be closed programmatically, so any required function
33635                                    should be called by the same code after it closes the dialog.
33636 icon              String           A CSS class that provides a background image to be used as an icon for
33637                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33638 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33639 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33640 modal             Boolean          False to allow user interaction with the page while the message box is
33641                                    displayed (defaults to true)
33642 msg               String           A string that will replace the existing message box body text (defaults
33643                                    to the XHTML-compliant non-breaking space character '&#160;')
33644 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33645 progress          Boolean          True to display a progress bar (defaults to false)
33646 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33647 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33648 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33649 title             String           The title text
33650 value             String           The string value to set into the active textbox element if displayed
33651 wait              Boolean          True to display a progress bar (defaults to false)
33652 width             Number           The width of the dialog in pixels
33653 </pre>
33654          *
33655          * Example usage:
33656          * <pre><code>
33657 Roo.Msg.show({
33658    title: 'Address',
33659    msg: 'Please enter your address:',
33660    width: 300,
33661    buttons: Roo.MessageBox.OKCANCEL,
33662    multiline: true,
33663    fn: saveAddress,
33664    animEl: 'addAddressBtn'
33665 });
33666 </code></pre>
33667          * @param {Object} config Configuration options
33668          * @return {Roo.MessageBox} This message box
33669          */
33670         show : function(options)
33671         {
33672             
33673             // this causes nightmares if you show one dialog after another
33674             // especially on callbacks..
33675              
33676             if(this.isVisible()){
33677                 
33678                 this.hide();
33679                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33680                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33681                 Roo.log("New Dialog Message:" +  options.msg )
33682                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33683                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33684                 
33685             }
33686             var d = this.getDialog();
33687             opt = options;
33688             d.setTitle(opt.title || "&#160;");
33689             d.close.setDisplayed(opt.closable !== false);
33690             activeTextEl = textboxEl;
33691             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33692             if(opt.prompt){
33693                 if(opt.multiline){
33694                     textboxEl.hide();
33695                     textareaEl.show();
33696                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33697                         opt.multiline : this.defaultTextHeight);
33698                     activeTextEl = textareaEl;
33699                 }else{
33700                     textboxEl.show();
33701                     textareaEl.hide();
33702                 }
33703             }else{
33704                 textboxEl.hide();
33705                 textareaEl.hide();
33706             }
33707             progressEl.setDisplayed(opt.progress === true);
33708             this.updateProgress(0);
33709             activeTextEl.dom.value = opt.value || "";
33710             if(opt.prompt){
33711                 dlg.setDefaultButton(activeTextEl);
33712             }else{
33713                 var bs = opt.buttons;
33714                 var db = null;
33715                 if(bs && bs.ok){
33716                     db = buttons["ok"];
33717                 }else if(bs && bs.yes){
33718                     db = buttons["yes"];
33719                 }
33720                 dlg.setDefaultButton(db);
33721             }
33722             bwidth = updateButtons(opt.buttons);
33723             this.updateText(opt.msg);
33724             if(opt.cls){
33725                 d.el.addClass(opt.cls);
33726             }
33727             d.proxyDrag = opt.proxyDrag === true;
33728             d.modal = opt.modal !== false;
33729             d.mask = opt.modal !== false ? mask : false;
33730             if(!d.isVisible()){
33731                 // force it to the end of the z-index stack so it gets a cursor in FF
33732                 document.body.appendChild(dlg.el.dom);
33733                 d.animateTarget = null;
33734                 d.show(options.animEl);
33735             }
33736             return this;
33737         },
33738
33739         /**
33740          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33741          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33742          * and closing the message box when the process is complete.
33743          * @param {String} title The title bar text
33744          * @param {String} msg The message box body text
33745          * @return {Roo.MessageBox} This message box
33746          */
33747         progress : function(title, msg){
33748             this.show({
33749                 title : title,
33750                 msg : msg,
33751                 buttons: false,
33752                 progress:true,
33753                 closable:false,
33754                 minWidth: this.minProgressWidth,
33755                 modal : true
33756             });
33757             return this;
33758         },
33759
33760         /**
33761          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33762          * If a callback function is passed it will be called after the user clicks the button, and the
33763          * id of the button that was clicked will be passed as the only parameter to the callback
33764          * (could also be the top-right close button).
33765          * @param {String} title The title bar text
33766          * @param {String} msg The message box body text
33767          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33768          * @param {Object} scope (optional) The scope of the callback function
33769          * @return {Roo.MessageBox} This message box
33770          */
33771         alert : function(title, msg, fn, scope){
33772             this.show({
33773                 title : title,
33774                 msg : msg,
33775                 buttons: this.OK,
33776                 fn: fn,
33777                 scope : scope,
33778                 modal : true
33779             });
33780             return this;
33781         },
33782
33783         /**
33784          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33785          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33786          * You are responsible for closing the message box when the process is complete.
33787          * @param {String} msg The message box body text
33788          * @param {String} title (optional) The title bar text
33789          * @return {Roo.MessageBox} This message box
33790          */
33791         wait : function(msg, title){
33792             this.show({
33793                 title : title,
33794                 msg : msg,
33795                 buttons: false,
33796                 closable:false,
33797                 progress:true,
33798                 modal:true,
33799                 width:300,
33800                 wait:true
33801             });
33802             waitTimer = Roo.TaskMgr.start({
33803                 run: function(i){
33804                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33805                 },
33806                 interval: 1000
33807             });
33808             return this;
33809         },
33810
33811         /**
33812          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33813          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33814          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33815          * @param {String} title The title bar text
33816          * @param {String} msg The message box body text
33817          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33818          * @param {Object} scope (optional) The scope of the callback function
33819          * @return {Roo.MessageBox} This message box
33820          */
33821         confirm : function(title, msg, fn, scope){
33822             this.show({
33823                 title : title,
33824                 msg : msg,
33825                 buttons: this.YESNO,
33826                 fn: fn,
33827                 scope : scope,
33828                 modal : true
33829             });
33830             return this;
33831         },
33832
33833         /**
33834          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33835          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33836          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33837          * (could also be the top-right close button) and the text that was entered will be passed as the two
33838          * parameters to the callback.
33839          * @param {String} title The title bar text
33840          * @param {String} msg The message box body text
33841          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33842          * @param {Object} scope (optional) The scope of the callback function
33843          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33844          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33845          * @return {Roo.MessageBox} This message box
33846          */
33847         prompt : function(title, msg, fn, scope, multiline){
33848             this.show({
33849                 title : title,
33850                 msg : msg,
33851                 buttons: this.OKCANCEL,
33852                 fn: fn,
33853                 minWidth:250,
33854                 scope : scope,
33855                 prompt:true,
33856                 multiline: multiline,
33857                 modal : true
33858             });
33859             return this;
33860         },
33861
33862         /**
33863          * Button config that displays a single OK button
33864          * @type Object
33865          */
33866         OK : {ok:true},
33867         /**
33868          * Button config that displays Yes and No buttons
33869          * @type Object
33870          */
33871         YESNO : {yes:true, no:true},
33872         /**
33873          * Button config that displays OK and Cancel buttons
33874          * @type Object
33875          */
33876         OKCANCEL : {ok:true, cancel:true},
33877         /**
33878          * Button config that displays Yes, No and Cancel buttons
33879          * @type Object
33880          */
33881         YESNOCANCEL : {yes:true, no:true, cancel:true},
33882
33883         /**
33884          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33885          * @type Number
33886          */
33887         defaultTextHeight : 75,
33888         /**
33889          * The maximum width in pixels of the message box (defaults to 600)
33890          * @type Number
33891          */
33892         maxWidth : 600,
33893         /**
33894          * The minimum width in pixels of the message box (defaults to 100)
33895          * @type Number
33896          */
33897         minWidth : 100,
33898         /**
33899          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33900          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33901          * @type Number
33902          */
33903         minProgressWidth : 250,
33904         /**
33905          * An object containing the default button text strings that can be overriden for localized language support.
33906          * Supported properties are: ok, cancel, yes and no.
33907          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33908          * @type Object
33909          */
33910         buttonText : {
33911             ok : "OK",
33912             cancel : "Cancel",
33913             yes : "Yes",
33914             no : "No"
33915         }
33916     };
33917 }();
33918
33919 /**
33920  * Shorthand for {@link Roo.MessageBox}
33921  */
33922 Roo.Msg = Roo.MessageBox;/*
33923  * Based on:
33924  * Ext JS Library 1.1.1
33925  * Copyright(c) 2006-2007, Ext JS, LLC.
33926  *
33927  * Originally Released Under LGPL - original licence link has changed is not relivant.
33928  *
33929  * Fork - LGPL
33930  * <script type="text/javascript">
33931  */
33932 /**
33933  * @class Roo.QuickTips
33934  * Provides attractive and customizable tooltips for any element.
33935  * @singleton
33936  */
33937 Roo.QuickTips = function(){
33938     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33939     var ce, bd, xy, dd;
33940     var visible = false, disabled = true, inited = false;
33941     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33942     
33943     var onOver = function(e){
33944         if(disabled){
33945             return;
33946         }
33947         var t = e.getTarget();
33948         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33949             return;
33950         }
33951         if(ce && t == ce.el){
33952             clearTimeout(hideProc);
33953             return;
33954         }
33955         if(t && tagEls[t.id]){
33956             tagEls[t.id].el = t;
33957             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33958             return;
33959         }
33960         var ttp, et = Roo.fly(t);
33961         var ns = cfg.namespace;
33962         if(tm.interceptTitles && t.title){
33963             ttp = t.title;
33964             t.qtip = ttp;
33965             t.removeAttribute("title");
33966             e.preventDefault();
33967         }else{
33968             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33969         }
33970         if(ttp){
33971             showProc = show.defer(tm.showDelay, tm, [{
33972                 el: t, 
33973                 text: ttp.replace(/\\n/g,'<br/>'),
33974                 width: et.getAttributeNS(ns, cfg.width),
33975                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33976                 title: et.getAttributeNS(ns, cfg.title),
33977                     cls: et.getAttributeNS(ns, cfg.cls)
33978             }]);
33979         }
33980     };
33981     
33982     var onOut = function(e){
33983         clearTimeout(showProc);
33984         var t = e.getTarget();
33985         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33986             hideProc = setTimeout(hide, tm.hideDelay);
33987         }
33988     };
33989     
33990     var onMove = function(e){
33991         if(disabled){
33992             return;
33993         }
33994         xy = e.getXY();
33995         xy[1] += 18;
33996         if(tm.trackMouse && ce){
33997             el.setXY(xy);
33998         }
33999     };
34000     
34001     var onDown = function(e){
34002         clearTimeout(showProc);
34003         clearTimeout(hideProc);
34004         if(!e.within(el)){
34005             if(tm.hideOnClick){
34006                 hide();
34007                 tm.disable();
34008                 tm.enable.defer(100, tm);
34009             }
34010         }
34011     };
34012     
34013     var getPad = function(){
34014         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34015     };
34016
34017     var show = function(o){
34018         if(disabled){
34019             return;
34020         }
34021         clearTimeout(dismissProc);
34022         ce = o;
34023         if(removeCls){ // in case manually hidden
34024             el.removeClass(removeCls);
34025             removeCls = null;
34026         }
34027         if(ce.cls){
34028             el.addClass(ce.cls);
34029             removeCls = ce.cls;
34030         }
34031         if(ce.title){
34032             tipTitle.update(ce.title);
34033             tipTitle.show();
34034         }else{
34035             tipTitle.update('');
34036             tipTitle.hide();
34037         }
34038         el.dom.style.width  = tm.maxWidth+'px';
34039         //tipBody.dom.style.width = '';
34040         tipBodyText.update(o.text);
34041         var p = getPad(), w = ce.width;
34042         if(!w){
34043             var td = tipBodyText.dom;
34044             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34045             if(aw > tm.maxWidth){
34046                 w = tm.maxWidth;
34047             }else if(aw < tm.minWidth){
34048                 w = tm.minWidth;
34049             }else{
34050                 w = aw;
34051             }
34052         }
34053         //tipBody.setWidth(w);
34054         el.setWidth(parseInt(w, 10) + p);
34055         if(ce.autoHide === false){
34056             close.setDisplayed(true);
34057             if(dd){
34058                 dd.unlock();
34059             }
34060         }else{
34061             close.setDisplayed(false);
34062             if(dd){
34063                 dd.lock();
34064             }
34065         }
34066         if(xy){
34067             el.avoidY = xy[1]-18;
34068             el.setXY(xy);
34069         }
34070         if(tm.animate){
34071             el.setOpacity(.1);
34072             el.setStyle("visibility", "visible");
34073             el.fadeIn({callback: afterShow});
34074         }else{
34075             afterShow();
34076         }
34077     };
34078     
34079     var afterShow = function(){
34080         if(ce){
34081             el.show();
34082             esc.enable();
34083             if(tm.autoDismiss && ce.autoHide !== false){
34084                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34085             }
34086         }
34087     };
34088     
34089     var hide = function(noanim){
34090         clearTimeout(dismissProc);
34091         clearTimeout(hideProc);
34092         ce = null;
34093         if(el.isVisible()){
34094             esc.disable();
34095             if(noanim !== true && tm.animate){
34096                 el.fadeOut({callback: afterHide});
34097             }else{
34098                 afterHide();
34099             } 
34100         }
34101     };
34102     
34103     var afterHide = function(){
34104         el.hide();
34105         if(removeCls){
34106             el.removeClass(removeCls);
34107             removeCls = null;
34108         }
34109     };
34110     
34111     return {
34112         /**
34113         * @cfg {Number} minWidth
34114         * The minimum width of the quick tip (defaults to 40)
34115         */
34116        minWidth : 40,
34117         /**
34118         * @cfg {Number} maxWidth
34119         * The maximum width of the quick tip (defaults to 300)
34120         */
34121        maxWidth : 300,
34122         /**
34123         * @cfg {Boolean} interceptTitles
34124         * True to automatically use the element's DOM title value if available (defaults to false)
34125         */
34126        interceptTitles : false,
34127         /**
34128         * @cfg {Boolean} trackMouse
34129         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34130         */
34131        trackMouse : false,
34132         /**
34133         * @cfg {Boolean} hideOnClick
34134         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34135         */
34136        hideOnClick : true,
34137         /**
34138         * @cfg {Number} showDelay
34139         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34140         */
34141        showDelay : 500,
34142         /**
34143         * @cfg {Number} hideDelay
34144         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34145         */
34146        hideDelay : 200,
34147         /**
34148         * @cfg {Boolean} autoHide
34149         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34150         * Used in conjunction with hideDelay.
34151         */
34152        autoHide : true,
34153         /**
34154         * @cfg {Boolean}
34155         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34156         * (defaults to true).  Used in conjunction with autoDismissDelay.
34157         */
34158        autoDismiss : true,
34159         /**
34160         * @cfg {Number}
34161         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34162         */
34163        autoDismissDelay : 5000,
34164        /**
34165         * @cfg {Boolean} animate
34166         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34167         */
34168        animate : false,
34169
34170        /**
34171         * @cfg {String} title
34172         * Title text to display (defaults to '').  This can be any valid HTML markup.
34173         */
34174         title: '',
34175        /**
34176         * @cfg {String} text
34177         * Body text to display (defaults to '').  This can be any valid HTML markup.
34178         */
34179         text : '',
34180        /**
34181         * @cfg {String} cls
34182         * A CSS class to apply to the base quick tip element (defaults to '').
34183         */
34184         cls : '',
34185        /**
34186         * @cfg {Number} width
34187         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34188         * minWidth or maxWidth.
34189         */
34190         width : null,
34191
34192     /**
34193      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34194      * or display QuickTips in a page.
34195      */
34196        init : function(){
34197           tm = Roo.QuickTips;
34198           cfg = tm.tagConfig;
34199           if(!inited){
34200               if(!Roo.isReady){ // allow calling of init() before onReady
34201                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34202                   return;
34203               }
34204               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34205               el.fxDefaults = {stopFx: true};
34206               // maximum custom styling
34207               //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>');
34208               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>');              
34209               tipTitle = el.child('h3');
34210               tipTitle.enableDisplayMode("block");
34211               tipBody = el.child('div.x-tip-bd');
34212               tipBodyText = el.child('div.x-tip-bd-inner');
34213               //bdLeft = el.child('div.x-tip-bd-left');
34214               //bdRight = el.child('div.x-tip-bd-right');
34215               close = el.child('div.x-tip-close');
34216               close.enableDisplayMode("block");
34217               close.on("click", hide);
34218               var d = Roo.get(document);
34219               d.on("mousedown", onDown);
34220               d.on("mouseover", onOver);
34221               d.on("mouseout", onOut);
34222               d.on("mousemove", onMove);
34223               esc = d.addKeyListener(27, hide);
34224               esc.disable();
34225               if(Roo.dd.DD){
34226                   dd = el.initDD("default", null, {
34227                       onDrag : function(){
34228                           el.sync();  
34229                       }
34230                   });
34231                   dd.setHandleElId(tipTitle.id);
34232                   dd.lock();
34233               }
34234               inited = true;
34235           }
34236           this.enable(); 
34237        },
34238
34239     /**
34240      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34241      * are supported:
34242      * <pre>
34243 Property    Type                   Description
34244 ----------  ---------------------  ------------------------------------------------------------------------
34245 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34246      * </ul>
34247      * @param {Object} config The config object
34248      */
34249        register : function(config){
34250            var cs = config instanceof Array ? config : arguments;
34251            for(var i = 0, len = cs.length; i < len; i++) {
34252                var c = cs[i];
34253                var target = c.target;
34254                if(target){
34255                    if(target instanceof Array){
34256                        for(var j = 0, jlen = target.length; j < jlen; j++){
34257                            tagEls[target[j]] = c;
34258                        }
34259                    }else{
34260                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34261                    }
34262                }
34263            }
34264        },
34265
34266     /**
34267      * Removes this quick tip from its element and destroys it.
34268      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34269      */
34270        unregister : function(el){
34271            delete tagEls[Roo.id(el)];
34272        },
34273
34274     /**
34275      * Enable this quick tip.
34276      */
34277        enable : function(){
34278            if(inited && disabled){
34279                locks.pop();
34280                if(locks.length < 1){
34281                    disabled = false;
34282                }
34283            }
34284        },
34285
34286     /**
34287      * Disable this quick tip.
34288      */
34289        disable : function(){
34290           disabled = true;
34291           clearTimeout(showProc);
34292           clearTimeout(hideProc);
34293           clearTimeout(dismissProc);
34294           if(ce){
34295               hide(true);
34296           }
34297           locks.push(1);
34298        },
34299
34300     /**
34301      * Returns true if the quick tip is enabled, else false.
34302      */
34303        isEnabled : function(){
34304             return !disabled;
34305        },
34306
34307         // private
34308        tagConfig : {
34309            namespace : "roo", // was ext?? this may break..
34310            alt_namespace : "ext",
34311            attribute : "qtip",
34312            width : "width",
34313            target : "target",
34314            title : "qtitle",
34315            hide : "hide",
34316            cls : "qclass"
34317        }
34318    };
34319 }();
34320
34321 // backwards compat
34322 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34323  * Based on:
34324  * Ext JS Library 1.1.1
34325  * Copyright(c) 2006-2007, Ext JS, LLC.
34326  *
34327  * Originally Released Under LGPL - original licence link has changed is not relivant.
34328  *
34329  * Fork - LGPL
34330  * <script type="text/javascript">
34331  */
34332  
34333
34334 /**
34335  * @class Roo.tree.TreePanel
34336  * @extends Roo.data.Tree
34337
34338  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34339  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34340  * @cfg {Boolean} enableDD true to enable drag and drop
34341  * @cfg {Boolean} enableDrag true to enable just drag
34342  * @cfg {Boolean} enableDrop true to enable just drop
34343  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34344  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34345  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34346  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34347  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34348  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34349  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34350  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34351  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34352  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34353  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34354  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34355  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34356  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34357  * @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>
34358  * @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>
34359  * 
34360  * @constructor
34361  * @param {String/HTMLElement/Element} el The container element
34362  * @param {Object} config
34363  */
34364 Roo.tree.TreePanel = function(el, config){
34365     var root = false;
34366     var loader = false;
34367     if (config.root) {
34368         root = config.root;
34369         delete config.root;
34370     }
34371     if (config.loader) {
34372         loader = config.loader;
34373         delete config.loader;
34374     }
34375     
34376     Roo.apply(this, config);
34377     Roo.tree.TreePanel.superclass.constructor.call(this);
34378     this.el = Roo.get(el);
34379     this.el.addClass('x-tree');
34380     //console.log(root);
34381     if (root) {
34382         this.setRootNode( Roo.factory(root, Roo.tree));
34383     }
34384     if (loader) {
34385         this.loader = Roo.factory(loader, Roo.tree);
34386     }
34387    /**
34388     * Read-only. The id of the container element becomes this TreePanel's id.
34389     */
34390     this.id = this.el.id;
34391     this.addEvents({
34392         /**
34393         * @event beforeload
34394         * Fires before a node is loaded, return false to cancel
34395         * @param {Node} node The node being loaded
34396         */
34397         "beforeload" : true,
34398         /**
34399         * @event load
34400         * Fires when a node is loaded
34401         * @param {Node} node The node that was loaded
34402         */
34403         "load" : true,
34404         /**
34405         * @event textchange
34406         * Fires when the text for a node is changed
34407         * @param {Node} node The node
34408         * @param {String} text The new text
34409         * @param {String} oldText The old text
34410         */
34411         "textchange" : true,
34412         /**
34413         * @event beforeexpand
34414         * Fires before a node is expanded, return false to cancel.
34415         * @param {Node} node The node
34416         * @param {Boolean} deep
34417         * @param {Boolean} anim
34418         */
34419         "beforeexpand" : true,
34420         /**
34421         * @event beforecollapse
34422         * Fires before a node is collapsed, return false to cancel.
34423         * @param {Node} node The node
34424         * @param {Boolean} deep
34425         * @param {Boolean} anim
34426         */
34427         "beforecollapse" : true,
34428         /**
34429         * @event expand
34430         * Fires when a node is expanded
34431         * @param {Node} node The node
34432         */
34433         "expand" : true,
34434         /**
34435         * @event disabledchange
34436         * Fires when the disabled status of a node changes
34437         * @param {Node} node The node
34438         * @param {Boolean} disabled
34439         */
34440         "disabledchange" : true,
34441         /**
34442         * @event collapse
34443         * Fires when a node is collapsed
34444         * @param {Node} node The node
34445         */
34446         "collapse" : true,
34447         /**
34448         * @event beforeclick
34449         * Fires before click processing on a node. Return false to cancel the default action.
34450         * @param {Node} node The node
34451         * @param {Roo.EventObject} e The event object
34452         */
34453         "beforeclick":true,
34454         /**
34455         * @event checkchange
34456         * Fires when a node with a checkbox's checked property changes
34457         * @param {Node} this This node
34458         * @param {Boolean} checked
34459         */
34460         "checkchange":true,
34461         /**
34462         * @event click
34463         * Fires when a node is clicked
34464         * @param {Node} node The node
34465         * @param {Roo.EventObject} e The event object
34466         */
34467         "click":true,
34468         /**
34469         * @event dblclick
34470         * Fires when a node is double clicked
34471         * @param {Node} node The node
34472         * @param {Roo.EventObject} e The event object
34473         */
34474         "dblclick":true,
34475         /**
34476         * @event contextmenu
34477         * Fires when a node is right clicked
34478         * @param {Node} node The node
34479         * @param {Roo.EventObject} e The event object
34480         */
34481         "contextmenu":true,
34482         /**
34483         * @event beforechildrenrendered
34484         * Fires right before the child nodes for a node are rendered
34485         * @param {Node} node The node
34486         */
34487         "beforechildrenrendered":true,
34488         /**
34489         * @event startdrag
34490         * Fires when a node starts being dragged
34491         * @param {Roo.tree.TreePanel} this
34492         * @param {Roo.tree.TreeNode} node
34493         * @param {event} e The raw browser event
34494         */ 
34495        "startdrag" : true,
34496        /**
34497         * @event enddrag
34498         * Fires when a drag operation is complete
34499         * @param {Roo.tree.TreePanel} this
34500         * @param {Roo.tree.TreeNode} node
34501         * @param {event} e The raw browser event
34502         */
34503        "enddrag" : true,
34504        /**
34505         * @event dragdrop
34506         * Fires when a dragged node is dropped on a valid DD target
34507         * @param {Roo.tree.TreePanel} this
34508         * @param {Roo.tree.TreeNode} node
34509         * @param {DD} dd The dd it was dropped on
34510         * @param {event} e The raw browser event
34511         */
34512        "dragdrop" : true,
34513        /**
34514         * @event beforenodedrop
34515         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34516         * passed to handlers has the following properties:<br />
34517         * <ul style="padding:5px;padding-left:16px;">
34518         * <li>tree - The TreePanel</li>
34519         * <li>target - The node being targeted for the drop</li>
34520         * <li>data - The drag data from the drag source</li>
34521         * <li>point - The point of the drop - append, above or below</li>
34522         * <li>source - The drag source</li>
34523         * <li>rawEvent - Raw mouse event</li>
34524         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34525         * to be inserted by setting them on this object.</li>
34526         * <li>cancel - Set this to true to cancel the drop.</li>
34527         * </ul>
34528         * @param {Object} dropEvent
34529         */
34530        "beforenodedrop" : true,
34531        /**
34532         * @event nodedrop
34533         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34534         * passed to handlers has the following properties:<br />
34535         * <ul style="padding:5px;padding-left:16px;">
34536         * <li>tree - The TreePanel</li>
34537         * <li>target - The node being targeted for the drop</li>
34538         * <li>data - The drag data from the drag source</li>
34539         * <li>point - The point of the drop - append, above or below</li>
34540         * <li>source - The drag source</li>
34541         * <li>rawEvent - Raw mouse event</li>
34542         * <li>dropNode - Dropped node(s).</li>
34543         * </ul>
34544         * @param {Object} dropEvent
34545         */
34546        "nodedrop" : true,
34547         /**
34548         * @event nodedragover
34549         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34550         * passed to handlers has the following properties:<br />
34551         * <ul style="padding:5px;padding-left:16px;">
34552         * <li>tree - The TreePanel</li>
34553         * <li>target - The node being targeted for the drop</li>
34554         * <li>data - The drag data from the drag source</li>
34555         * <li>point - The point of the drop - append, above or below</li>
34556         * <li>source - The drag source</li>
34557         * <li>rawEvent - Raw mouse event</li>
34558         * <li>dropNode - Drop node(s) provided by the source.</li>
34559         * <li>cancel - Set this to true to signal drop not allowed.</li>
34560         * </ul>
34561         * @param {Object} dragOverEvent
34562         */
34563        "nodedragover" : true,
34564        /**
34565         * @event appendnode
34566         * Fires when append node to the tree
34567         * @param {Roo.tree.TreePanel} this
34568         * @param {Roo.tree.TreeNode} node
34569         * @param {Number} index The index of the newly appended node
34570         */
34571        "appendnode" : true
34572         
34573     });
34574     if(this.singleExpand){
34575        this.on("beforeexpand", this.restrictExpand, this);
34576     }
34577     if (this.editor) {
34578         this.editor.tree = this;
34579         this.editor = Roo.factory(this.editor, Roo.tree);
34580     }
34581     
34582     if (this.selModel) {
34583         this.selModel = Roo.factory(this.selModel, Roo.tree);
34584     }
34585    
34586 };
34587 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34588     rootVisible : true,
34589     animate: Roo.enableFx,
34590     lines : true,
34591     enableDD : false,
34592     hlDrop : Roo.enableFx,
34593   
34594     renderer: false,
34595     
34596     rendererTip: false,
34597     // private
34598     restrictExpand : function(node){
34599         var p = node.parentNode;
34600         if(p){
34601             if(p.expandedChild && p.expandedChild.parentNode == p){
34602                 p.expandedChild.collapse();
34603             }
34604             p.expandedChild = node;
34605         }
34606     },
34607
34608     // private override
34609     setRootNode : function(node){
34610         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34611         if(!this.rootVisible){
34612             node.ui = new Roo.tree.RootTreeNodeUI(node);
34613         }
34614         return node;
34615     },
34616
34617     /**
34618      * Returns the container element for this TreePanel
34619      */
34620     getEl : function(){
34621         return this.el;
34622     },
34623
34624     /**
34625      * Returns the default TreeLoader for this TreePanel
34626      */
34627     getLoader : function(){
34628         return this.loader;
34629     },
34630
34631     /**
34632      * Expand all nodes
34633      */
34634     expandAll : function(){
34635         this.root.expand(true);
34636     },
34637
34638     /**
34639      * Collapse all nodes
34640      */
34641     collapseAll : function(){
34642         this.root.collapse(true);
34643     },
34644
34645     /**
34646      * Returns the selection model used by this TreePanel
34647      */
34648     getSelectionModel : function(){
34649         if(!this.selModel){
34650             this.selModel = new Roo.tree.DefaultSelectionModel();
34651         }
34652         return this.selModel;
34653     },
34654
34655     /**
34656      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34657      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34658      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34659      * @return {Array}
34660      */
34661     getChecked : function(a, startNode){
34662         startNode = startNode || this.root;
34663         var r = [];
34664         var f = function(){
34665             if(this.attributes.checked){
34666                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34667             }
34668         }
34669         startNode.cascade(f);
34670         return r;
34671     },
34672
34673     /**
34674      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34675      * @param {String} path
34676      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34677      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34678      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34679      */
34680     expandPath : function(path, attr, callback){
34681         attr = attr || "id";
34682         var keys = path.split(this.pathSeparator);
34683         var curNode = this.root;
34684         if(curNode.attributes[attr] != keys[1]){ // invalid root
34685             if(callback){
34686                 callback(false, null);
34687             }
34688             return;
34689         }
34690         var index = 1;
34691         var f = function(){
34692             if(++index == keys.length){
34693                 if(callback){
34694                     callback(true, curNode);
34695                 }
34696                 return;
34697             }
34698             var c = curNode.findChild(attr, keys[index]);
34699             if(!c){
34700                 if(callback){
34701                     callback(false, curNode);
34702                 }
34703                 return;
34704             }
34705             curNode = c;
34706             c.expand(false, false, f);
34707         };
34708         curNode.expand(false, false, f);
34709     },
34710
34711     /**
34712      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34713      * @param {String} path
34714      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34715      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34716      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34717      */
34718     selectPath : function(path, attr, callback){
34719         attr = attr || "id";
34720         var keys = path.split(this.pathSeparator);
34721         var v = keys.pop();
34722         if(keys.length > 0){
34723             var f = function(success, node){
34724                 if(success && node){
34725                     var n = node.findChild(attr, v);
34726                     if(n){
34727                         n.select();
34728                         if(callback){
34729                             callback(true, n);
34730                         }
34731                     }else if(callback){
34732                         callback(false, n);
34733                     }
34734                 }else{
34735                     if(callback){
34736                         callback(false, n);
34737                     }
34738                 }
34739             };
34740             this.expandPath(keys.join(this.pathSeparator), attr, f);
34741         }else{
34742             this.root.select();
34743             if(callback){
34744                 callback(true, this.root);
34745             }
34746         }
34747     },
34748
34749     getTreeEl : function(){
34750         return this.el;
34751     },
34752
34753     /**
34754      * Trigger rendering of this TreePanel
34755      */
34756     render : function(){
34757         if (this.innerCt) {
34758             return this; // stop it rendering more than once!!
34759         }
34760         
34761         this.innerCt = this.el.createChild({tag:"ul",
34762                cls:"x-tree-root-ct " +
34763                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34764
34765         if(this.containerScroll){
34766             Roo.dd.ScrollManager.register(this.el);
34767         }
34768         if((this.enableDD || this.enableDrop) && !this.dropZone){
34769            /**
34770             * The dropZone used by this tree if drop is enabled
34771             * @type Roo.tree.TreeDropZone
34772             */
34773              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34774                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34775            });
34776         }
34777         if((this.enableDD || this.enableDrag) && !this.dragZone){
34778            /**
34779             * The dragZone used by this tree if drag is enabled
34780             * @type Roo.tree.TreeDragZone
34781             */
34782             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34783                ddGroup: this.ddGroup || "TreeDD",
34784                scroll: this.ddScroll
34785            });
34786         }
34787         this.getSelectionModel().init(this);
34788         if (!this.root) {
34789             Roo.log("ROOT not set in tree");
34790             return this;
34791         }
34792         this.root.render();
34793         if(!this.rootVisible){
34794             this.root.renderChildren();
34795         }
34796         return this;
34797     }
34798 });/*
34799  * Based on:
34800  * Ext JS Library 1.1.1
34801  * Copyright(c) 2006-2007, Ext JS, LLC.
34802  *
34803  * Originally Released Under LGPL - original licence link has changed is not relivant.
34804  *
34805  * Fork - LGPL
34806  * <script type="text/javascript">
34807  */
34808  
34809
34810 /**
34811  * @class Roo.tree.DefaultSelectionModel
34812  * @extends Roo.util.Observable
34813  * The default single selection for a TreePanel.
34814  * @param {Object} cfg Configuration
34815  */
34816 Roo.tree.DefaultSelectionModel = function(cfg){
34817    this.selNode = null;
34818    
34819    
34820    
34821    this.addEvents({
34822        /**
34823         * @event selectionchange
34824         * Fires when the selected node changes
34825         * @param {DefaultSelectionModel} this
34826         * @param {TreeNode} node the new selection
34827         */
34828        "selectionchange" : true,
34829
34830        /**
34831         * @event beforeselect
34832         * Fires before the selected node changes, return false to cancel the change
34833         * @param {DefaultSelectionModel} this
34834         * @param {TreeNode} node the new selection
34835         * @param {TreeNode} node the old selection
34836         */
34837        "beforeselect" : true
34838    });
34839    
34840     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34841 };
34842
34843 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34844     init : function(tree){
34845         this.tree = tree;
34846         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34847         tree.on("click", this.onNodeClick, this);
34848     },
34849     
34850     onNodeClick : function(node, e){
34851         if (e.ctrlKey && this.selNode == node)  {
34852             this.unselect(node);
34853             return;
34854         }
34855         this.select(node);
34856     },
34857     
34858     /**
34859      * Select a node.
34860      * @param {TreeNode} node The node to select
34861      * @return {TreeNode} The selected node
34862      */
34863     select : function(node){
34864         var last = this.selNode;
34865         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34866             if(last){
34867                 last.ui.onSelectedChange(false);
34868             }
34869             this.selNode = node;
34870             node.ui.onSelectedChange(true);
34871             this.fireEvent("selectionchange", this, node, last);
34872         }
34873         return node;
34874     },
34875     
34876     /**
34877      * Deselect a node.
34878      * @param {TreeNode} node The node to unselect
34879      */
34880     unselect : function(node){
34881         if(this.selNode == node){
34882             this.clearSelections();
34883         }    
34884     },
34885     
34886     /**
34887      * Clear all selections
34888      */
34889     clearSelections : function(){
34890         var n = this.selNode;
34891         if(n){
34892             n.ui.onSelectedChange(false);
34893             this.selNode = null;
34894             this.fireEvent("selectionchange", this, null);
34895         }
34896         return n;
34897     },
34898     
34899     /**
34900      * Get the selected node
34901      * @return {TreeNode} The selected node
34902      */
34903     getSelectedNode : function(){
34904         return this.selNode;    
34905     },
34906     
34907     /**
34908      * Returns true if the node is selected
34909      * @param {TreeNode} node The node to check
34910      * @return {Boolean}
34911      */
34912     isSelected : function(node){
34913         return this.selNode == node;  
34914     },
34915
34916     /**
34917      * Selects the node above the selected node in the tree, intelligently walking the nodes
34918      * @return TreeNode The new selection
34919      */
34920     selectPrevious : function(){
34921         var s = this.selNode || this.lastSelNode;
34922         if(!s){
34923             return null;
34924         }
34925         var ps = s.previousSibling;
34926         if(ps){
34927             if(!ps.isExpanded() || ps.childNodes.length < 1){
34928                 return this.select(ps);
34929             } else{
34930                 var lc = ps.lastChild;
34931                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34932                     lc = lc.lastChild;
34933                 }
34934                 return this.select(lc);
34935             }
34936         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34937             return this.select(s.parentNode);
34938         }
34939         return null;
34940     },
34941
34942     /**
34943      * Selects the node above the selected node in the tree, intelligently walking the nodes
34944      * @return TreeNode The new selection
34945      */
34946     selectNext : function(){
34947         var s = this.selNode || this.lastSelNode;
34948         if(!s){
34949             return null;
34950         }
34951         if(s.firstChild && s.isExpanded()){
34952              return this.select(s.firstChild);
34953          }else if(s.nextSibling){
34954              return this.select(s.nextSibling);
34955          }else if(s.parentNode){
34956             var newS = null;
34957             s.parentNode.bubble(function(){
34958                 if(this.nextSibling){
34959                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34960                     return false;
34961                 }
34962             });
34963             return newS;
34964          }
34965         return null;
34966     },
34967
34968     onKeyDown : function(e){
34969         var s = this.selNode || this.lastSelNode;
34970         // undesirable, but required
34971         var sm = this;
34972         if(!s){
34973             return;
34974         }
34975         var k = e.getKey();
34976         switch(k){
34977              case e.DOWN:
34978                  e.stopEvent();
34979                  this.selectNext();
34980              break;
34981              case e.UP:
34982                  e.stopEvent();
34983                  this.selectPrevious();
34984              break;
34985              case e.RIGHT:
34986                  e.preventDefault();
34987                  if(s.hasChildNodes()){
34988                      if(!s.isExpanded()){
34989                          s.expand();
34990                      }else if(s.firstChild){
34991                          this.select(s.firstChild, e);
34992                      }
34993                  }
34994              break;
34995              case e.LEFT:
34996                  e.preventDefault();
34997                  if(s.hasChildNodes() && s.isExpanded()){
34998                      s.collapse();
34999                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35000                      this.select(s.parentNode, e);
35001                  }
35002              break;
35003         };
35004     }
35005 });
35006
35007 /**
35008  * @class Roo.tree.MultiSelectionModel
35009  * @extends Roo.util.Observable
35010  * Multi selection for a TreePanel.
35011  * @param {Object} cfg Configuration
35012  */
35013 Roo.tree.MultiSelectionModel = function(){
35014    this.selNodes = [];
35015    this.selMap = {};
35016    this.addEvents({
35017        /**
35018         * @event selectionchange
35019         * Fires when the selected nodes change
35020         * @param {MultiSelectionModel} this
35021         * @param {Array} nodes Array of the selected nodes
35022         */
35023        "selectionchange" : true
35024    });
35025    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35026    
35027 };
35028
35029 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35030     init : function(tree){
35031         this.tree = tree;
35032         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35033         tree.on("click", this.onNodeClick, this);
35034     },
35035     
35036     onNodeClick : function(node, e){
35037         this.select(node, e, e.ctrlKey);
35038     },
35039     
35040     /**
35041      * Select a node.
35042      * @param {TreeNode} node The node to select
35043      * @param {EventObject} e (optional) An event associated with the selection
35044      * @param {Boolean} keepExisting True to retain existing selections
35045      * @return {TreeNode} The selected node
35046      */
35047     select : function(node, e, keepExisting){
35048         if(keepExisting !== true){
35049             this.clearSelections(true);
35050         }
35051         if(this.isSelected(node)){
35052             this.lastSelNode = node;
35053             return node;
35054         }
35055         this.selNodes.push(node);
35056         this.selMap[node.id] = node;
35057         this.lastSelNode = node;
35058         node.ui.onSelectedChange(true);
35059         this.fireEvent("selectionchange", this, this.selNodes);
35060         return node;
35061     },
35062     
35063     /**
35064      * Deselect a node.
35065      * @param {TreeNode} node The node to unselect
35066      */
35067     unselect : function(node){
35068         if(this.selMap[node.id]){
35069             node.ui.onSelectedChange(false);
35070             var sn = this.selNodes;
35071             var index = -1;
35072             if(sn.indexOf){
35073                 index = sn.indexOf(node);
35074             }else{
35075                 for(var i = 0, len = sn.length; i < len; i++){
35076                     if(sn[i] == node){
35077                         index = i;
35078                         break;
35079                     }
35080                 }
35081             }
35082             if(index != -1){
35083                 this.selNodes.splice(index, 1);
35084             }
35085             delete this.selMap[node.id];
35086             this.fireEvent("selectionchange", this, this.selNodes);
35087         }
35088     },
35089     
35090     /**
35091      * Clear all selections
35092      */
35093     clearSelections : function(suppressEvent){
35094         var sn = this.selNodes;
35095         if(sn.length > 0){
35096             for(var i = 0, len = sn.length; i < len; i++){
35097                 sn[i].ui.onSelectedChange(false);
35098             }
35099             this.selNodes = [];
35100             this.selMap = {};
35101             if(suppressEvent !== true){
35102                 this.fireEvent("selectionchange", this, this.selNodes);
35103             }
35104         }
35105     },
35106     
35107     /**
35108      * Returns true if the node is selected
35109      * @param {TreeNode} node The node to check
35110      * @return {Boolean}
35111      */
35112     isSelected : function(node){
35113         return this.selMap[node.id] ? true : false;  
35114     },
35115     
35116     /**
35117      * Returns an array of the selected nodes
35118      * @return {Array}
35119      */
35120     getSelectedNodes : function(){
35121         return this.selNodes;    
35122     },
35123
35124     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35125
35126     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35127
35128     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35129 });/*
35130  * Based on:
35131  * Ext JS Library 1.1.1
35132  * Copyright(c) 2006-2007, Ext JS, LLC.
35133  *
35134  * Originally Released Under LGPL - original licence link has changed is not relivant.
35135  *
35136  * Fork - LGPL
35137  * <script type="text/javascript">
35138  */
35139  
35140 /**
35141  * @class Roo.tree.TreeNode
35142  * @extends Roo.data.Node
35143  * @cfg {String} text The text for this node
35144  * @cfg {Boolean} expanded true to start the node expanded
35145  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35146  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35147  * @cfg {Boolean} disabled true to start the node disabled
35148  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35149  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35150  * @cfg {String} cls A css class to be added to the node
35151  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35152  * @cfg {String} href URL of the link used for the node (defaults to #)
35153  * @cfg {String} hrefTarget target frame for the link
35154  * @cfg {String} qtip An Ext QuickTip for the node
35155  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35156  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35157  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35158  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35159  * (defaults to undefined with no checkbox rendered)
35160  * @constructor
35161  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35162  */
35163 Roo.tree.TreeNode = function(attributes){
35164     attributes = attributes || {};
35165     if(typeof attributes == "string"){
35166         attributes = {text: attributes};
35167     }
35168     this.childrenRendered = false;
35169     this.rendered = false;
35170     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35171     this.expanded = attributes.expanded === true;
35172     this.isTarget = attributes.isTarget !== false;
35173     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35174     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35175
35176     /**
35177      * Read-only. The text for this node. To change it use setText().
35178      * @type String
35179      */
35180     this.text = attributes.text;
35181     /**
35182      * True if this node is disabled.
35183      * @type Boolean
35184      */
35185     this.disabled = attributes.disabled === true;
35186
35187     this.addEvents({
35188         /**
35189         * @event textchange
35190         * Fires when the text for this node is changed
35191         * @param {Node} this This node
35192         * @param {String} text The new text
35193         * @param {String} oldText The old text
35194         */
35195         "textchange" : true,
35196         /**
35197         * @event beforeexpand
35198         * Fires before this node is expanded, return false to cancel.
35199         * @param {Node} this This node
35200         * @param {Boolean} deep
35201         * @param {Boolean} anim
35202         */
35203         "beforeexpand" : true,
35204         /**
35205         * @event beforecollapse
35206         * Fires before this node is collapsed, return false to cancel.
35207         * @param {Node} this This node
35208         * @param {Boolean} deep
35209         * @param {Boolean} anim
35210         */
35211         "beforecollapse" : true,
35212         /**
35213         * @event expand
35214         * Fires when this node is expanded
35215         * @param {Node} this This node
35216         */
35217         "expand" : true,
35218         /**
35219         * @event disabledchange
35220         * Fires when the disabled status of this node changes
35221         * @param {Node} this This node
35222         * @param {Boolean} disabled
35223         */
35224         "disabledchange" : true,
35225         /**
35226         * @event collapse
35227         * Fires when this node is collapsed
35228         * @param {Node} this This node
35229         */
35230         "collapse" : true,
35231         /**
35232         * @event beforeclick
35233         * Fires before click processing. Return false to cancel the default action.
35234         * @param {Node} this This node
35235         * @param {Roo.EventObject} e The event object
35236         */
35237         "beforeclick":true,
35238         /**
35239         * @event checkchange
35240         * Fires when a node with a checkbox's checked property changes
35241         * @param {Node} this This node
35242         * @param {Boolean} checked
35243         */
35244         "checkchange":true,
35245         /**
35246         * @event click
35247         * Fires when this node is clicked
35248         * @param {Node} this This node
35249         * @param {Roo.EventObject} e The event object
35250         */
35251         "click":true,
35252         /**
35253         * @event dblclick
35254         * Fires when this node is double clicked
35255         * @param {Node} this This node
35256         * @param {Roo.EventObject} e The event object
35257         */
35258         "dblclick":true,
35259         /**
35260         * @event contextmenu
35261         * Fires when this node is right clicked
35262         * @param {Node} this This node
35263         * @param {Roo.EventObject} e The event object
35264         */
35265         "contextmenu":true,
35266         /**
35267         * @event beforechildrenrendered
35268         * Fires right before the child nodes for this node are rendered
35269         * @param {Node} this This node
35270         */
35271         "beforechildrenrendered":true
35272     });
35273
35274     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35275
35276     /**
35277      * Read-only. The UI for this node
35278      * @type TreeNodeUI
35279      */
35280     this.ui = new uiClass(this);
35281     
35282     // finally support items[]
35283     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35284         return;
35285     }
35286     
35287     
35288     Roo.each(this.attributes.items, function(c) {
35289         this.appendChild(Roo.factory(c,Roo.Tree));
35290     }, this);
35291     delete this.attributes.items;
35292     
35293     
35294     
35295 };
35296 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35297     preventHScroll: true,
35298     /**
35299      * Returns true if this node is expanded
35300      * @return {Boolean}
35301      */
35302     isExpanded : function(){
35303         return this.expanded;
35304     },
35305
35306     /**
35307      * Returns the UI object for this node
35308      * @return {TreeNodeUI}
35309      */
35310     getUI : function(){
35311         return this.ui;
35312     },
35313
35314     // private override
35315     setFirstChild : function(node){
35316         var of = this.firstChild;
35317         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35318         if(this.childrenRendered && of && node != of){
35319             of.renderIndent(true, true);
35320         }
35321         if(this.rendered){
35322             this.renderIndent(true, true);
35323         }
35324     },
35325
35326     // private override
35327     setLastChild : function(node){
35328         var ol = this.lastChild;
35329         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35330         if(this.childrenRendered && ol && node != ol){
35331             ol.renderIndent(true, true);
35332         }
35333         if(this.rendered){
35334             this.renderIndent(true, true);
35335         }
35336     },
35337
35338     // these methods are overridden to provide lazy rendering support
35339     // private override
35340     appendChild : function()
35341     {
35342         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35343         if(node && this.childrenRendered){
35344             node.render();
35345         }
35346         this.ui.updateExpandIcon();
35347         return node;
35348     },
35349
35350     // private override
35351     removeChild : function(node){
35352         this.ownerTree.getSelectionModel().unselect(node);
35353         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35354         // if it's been rendered remove dom node
35355         if(this.childrenRendered){
35356             node.ui.remove();
35357         }
35358         if(this.childNodes.length < 1){
35359             this.collapse(false, false);
35360         }else{
35361             this.ui.updateExpandIcon();
35362         }
35363         if(!this.firstChild) {
35364             this.childrenRendered = false;
35365         }
35366         return node;
35367     },
35368
35369     // private override
35370     insertBefore : function(node, refNode){
35371         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35372         if(newNode && refNode && this.childrenRendered){
35373             node.render();
35374         }
35375         this.ui.updateExpandIcon();
35376         return newNode;
35377     },
35378
35379     /**
35380      * Sets the text for this node
35381      * @param {String} text
35382      */
35383     setText : function(text){
35384         var oldText = this.text;
35385         this.text = text;
35386         this.attributes.text = text;
35387         if(this.rendered){ // event without subscribing
35388             this.ui.onTextChange(this, text, oldText);
35389         }
35390         this.fireEvent("textchange", this, text, oldText);
35391     },
35392
35393     /**
35394      * Triggers selection of this node
35395      */
35396     select : function(){
35397         this.getOwnerTree().getSelectionModel().select(this);
35398     },
35399
35400     /**
35401      * Triggers deselection of this node
35402      */
35403     unselect : function(){
35404         this.getOwnerTree().getSelectionModel().unselect(this);
35405     },
35406
35407     /**
35408      * Returns true if this node is selected
35409      * @return {Boolean}
35410      */
35411     isSelected : function(){
35412         return this.getOwnerTree().getSelectionModel().isSelected(this);
35413     },
35414
35415     /**
35416      * Expand this node.
35417      * @param {Boolean} deep (optional) True to expand all children as well
35418      * @param {Boolean} anim (optional) false to cancel the default animation
35419      * @param {Function} callback (optional) A callback to be called when
35420      * expanding this node completes (does not wait for deep expand to complete).
35421      * Called with 1 parameter, this node.
35422      */
35423     expand : function(deep, anim, callback){
35424         if(!this.expanded){
35425             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35426                 return;
35427             }
35428             if(!this.childrenRendered){
35429                 this.renderChildren();
35430             }
35431             this.expanded = true;
35432             
35433             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35434                 this.ui.animExpand(function(){
35435                     this.fireEvent("expand", this);
35436                     if(typeof callback == "function"){
35437                         callback(this);
35438                     }
35439                     if(deep === true){
35440                         this.expandChildNodes(true);
35441                     }
35442                 }.createDelegate(this));
35443                 return;
35444             }else{
35445                 this.ui.expand();
35446                 this.fireEvent("expand", this);
35447                 if(typeof callback == "function"){
35448                     callback(this);
35449                 }
35450             }
35451         }else{
35452            if(typeof callback == "function"){
35453                callback(this);
35454            }
35455         }
35456         if(deep === true){
35457             this.expandChildNodes(true);
35458         }
35459     },
35460
35461     isHiddenRoot : function(){
35462         return this.isRoot && !this.getOwnerTree().rootVisible;
35463     },
35464
35465     /**
35466      * Collapse this node.
35467      * @param {Boolean} deep (optional) True to collapse all children as well
35468      * @param {Boolean} anim (optional) false to cancel the default animation
35469      */
35470     collapse : function(deep, anim){
35471         if(this.expanded && !this.isHiddenRoot()){
35472             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35473                 return;
35474             }
35475             this.expanded = false;
35476             if((this.getOwnerTree().animate && anim !== false) || anim){
35477                 this.ui.animCollapse(function(){
35478                     this.fireEvent("collapse", this);
35479                     if(deep === true){
35480                         this.collapseChildNodes(true);
35481                     }
35482                 }.createDelegate(this));
35483                 return;
35484             }else{
35485                 this.ui.collapse();
35486                 this.fireEvent("collapse", this);
35487             }
35488         }
35489         if(deep === true){
35490             var cs = this.childNodes;
35491             for(var i = 0, len = cs.length; i < len; i++) {
35492                 cs[i].collapse(true, false);
35493             }
35494         }
35495     },
35496
35497     // private
35498     delayedExpand : function(delay){
35499         if(!this.expandProcId){
35500             this.expandProcId = this.expand.defer(delay, this);
35501         }
35502     },
35503
35504     // private
35505     cancelExpand : function(){
35506         if(this.expandProcId){
35507             clearTimeout(this.expandProcId);
35508         }
35509         this.expandProcId = false;
35510     },
35511
35512     /**
35513      * Toggles expanded/collapsed state of the node
35514      */
35515     toggle : function(){
35516         if(this.expanded){
35517             this.collapse();
35518         }else{
35519             this.expand();
35520         }
35521     },
35522
35523     /**
35524      * Ensures all parent nodes are expanded
35525      */
35526     ensureVisible : function(callback){
35527         var tree = this.getOwnerTree();
35528         tree.expandPath(this.parentNode.getPath(), false, function(){
35529             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35530             Roo.callback(callback);
35531         }.createDelegate(this));
35532     },
35533
35534     /**
35535      * Expand all child nodes
35536      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35537      */
35538     expandChildNodes : function(deep){
35539         var cs = this.childNodes;
35540         for(var i = 0, len = cs.length; i < len; i++) {
35541                 cs[i].expand(deep);
35542         }
35543     },
35544
35545     /**
35546      * Collapse all child nodes
35547      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35548      */
35549     collapseChildNodes : function(deep){
35550         var cs = this.childNodes;
35551         for(var i = 0, len = cs.length; i < len; i++) {
35552                 cs[i].collapse(deep);
35553         }
35554     },
35555
35556     /**
35557      * Disables this node
35558      */
35559     disable : function(){
35560         this.disabled = true;
35561         this.unselect();
35562         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35563             this.ui.onDisableChange(this, true);
35564         }
35565         this.fireEvent("disabledchange", this, true);
35566     },
35567
35568     /**
35569      * Enables this node
35570      */
35571     enable : function(){
35572         this.disabled = false;
35573         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35574             this.ui.onDisableChange(this, false);
35575         }
35576         this.fireEvent("disabledchange", this, false);
35577     },
35578
35579     // private
35580     renderChildren : function(suppressEvent){
35581         if(suppressEvent !== false){
35582             this.fireEvent("beforechildrenrendered", this);
35583         }
35584         var cs = this.childNodes;
35585         for(var i = 0, len = cs.length; i < len; i++){
35586             cs[i].render(true);
35587         }
35588         this.childrenRendered = true;
35589     },
35590
35591     // private
35592     sort : function(fn, scope){
35593         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35594         if(this.childrenRendered){
35595             var cs = this.childNodes;
35596             for(var i = 0, len = cs.length; i < len; i++){
35597                 cs[i].render(true);
35598             }
35599         }
35600     },
35601
35602     // private
35603     render : function(bulkRender){
35604         this.ui.render(bulkRender);
35605         if(!this.rendered){
35606             this.rendered = true;
35607             if(this.expanded){
35608                 this.expanded = false;
35609                 this.expand(false, false);
35610             }
35611         }
35612     },
35613
35614     // private
35615     renderIndent : function(deep, refresh){
35616         if(refresh){
35617             this.ui.childIndent = null;
35618         }
35619         this.ui.renderIndent();
35620         if(deep === true && this.childrenRendered){
35621             var cs = this.childNodes;
35622             for(var i = 0, len = cs.length; i < len; i++){
35623                 cs[i].renderIndent(true, refresh);
35624             }
35625         }
35626     }
35627 });/*
35628  * Based on:
35629  * Ext JS Library 1.1.1
35630  * Copyright(c) 2006-2007, Ext JS, LLC.
35631  *
35632  * Originally Released Under LGPL - original licence link has changed is not relivant.
35633  *
35634  * Fork - LGPL
35635  * <script type="text/javascript">
35636  */
35637  
35638 /**
35639  * @class Roo.tree.AsyncTreeNode
35640  * @extends Roo.tree.TreeNode
35641  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35642  * @constructor
35643  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35644  */
35645  Roo.tree.AsyncTreeNode = function(config){
35646     this.loaded = false;
35647     this.loading = false;
35648     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35649     /**
35650     * @event beforeload
35651     * Fires before this node is loaded, return false to cancel
35652     * @param {Node} this This node
35653     */
35654     this.addEvents({'beforeload':true, 'load': true});
35655     /**
35656     * @event load
35657     * Fires when this node is loaded
35658     * @param {Node} this This node
35659     */
35660     /**
35661      * The loader used by this node (defaults to using the tree's defined loader)
35662      * @type TreeLoader
35663      * @property loader
35664      */
35665 };
35666 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35667     expand : function(deep, anim, callback){
35668         if(this.loading){ // if an async load is already running, waiting til it's done
35669             var timer;
35670             var f = function(){
35671                 if(!this.loading){ // done loading
35672                     clearInterval(timer);
35673                     this.expand(deep, anim, callback);
35674                 }
35675             }.createDelegate(this);
35676             timer = setInterval(f, 200);
35677             return;
35678         }
35679         if(!this.loaded){
35680             if(this.fireEvent("beforeload", this) === false){
35681                 return;
35682             }
35683             this.loading = true;
35684             this.ui.beforeLoad(this);
35685             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35686             if(loader){
35687                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35688                 return;
35689             }
35690         }
35691         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35692     },
35693     
35694     /**
35695      * Returns true if this node is currently loading
35696      * @return {Boolean}
35697      */
35698     isLoading : function(){
35699         return this.loading;  
35700     },
35701     
35702     loadComplete : function(deep, anim, callback){
35703         this.loading = false;
35704         this.loaded = true;
35705         this.ui.afterLoad(this);
35706         this.fireEvent("load", this);
35707         this.expand(deep, anim, callback);
35708     },
35709     
35710     /**
35711      * Returns true if this node has been loaded
35712      * @return {Boolean}
35713      */
35714     isLoaded : function(){
35715         return this.loaded;
35716     },
35717     
35718     hasChildNodes : function(){
35719         if(!this.isLeaf() && !this.loaded){
35720             return true;
35721         }else{
35722             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35723         }
35724     },
35725
35726     /**
35727      * Trigger a reload for this node
35728      * @param {Function} callback
35729      */
35730     reload : function(callback){
35731         this.collapse(false, false);
35732         while(this.firstChild){
35733             this.removeChild(this.firstChild);
35734         }
35735         this.childrenRendered = false;
35736         this.loaded = false;
35737         if(this.isHiddenRoot()){
35738             this.expanded = false;
35739         }
35740         this.expand(false, false, callback);
35741     }
35742 });/*
35743  * Based on:
35744  * Ext JS Library 1.1.1
35745  * Copyright(c) 2006-2007, Ext JS, LLC.
35746  *
35747  * Originally Released Under LGPL - original licence link has changed is not relivant.
35748  *
35749  * Fork - LGPL
35750  * <script type="text/javascript">
35751  */
35752  
35753 /**
35754  * @class Roo.tree.TreeNodeUI
35755  * @constructor
35756  * @param {Object} node The node to render
35757  * The TreeNode UI implementation is separate from the
35758  * tree implementation. Unless you are customizing the tree UI,
35759  * you should never have to use this directly.
35760  */
35761 Roo.tree.TreeNodeUI = function(node){
35762     this.node = node;
35763     this.rendered = false;
35764     this.animating = false;
35765     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35766 };
35767
35768 Roo.tree.TreeNodeUI.prototype = {
35769     removeChild : function(node){
35770         if(this.rendered){
35771             this.ctNode.removeChild(node.ui.getEl());
35772         }
35773     },
35774
35775     beforeLoad : function(){
35776          this.addClass("x-tree-node-loading");
35777     },
35778
35779     afterLoad : function(){
35780          this.removeClass("x-tree-node-loading");
35781     },
35782
35783     onTextChange : function(node, text, oldText){
35784         if(this.rendered){
35785             this.textNode.innerHTML = text;
35786         }
35787     },
35788
35789     onDisableChange : function(node, state){
35790         this.disabled = state;
35791         if(state){
35792             this.addClass("x-tree-node-disabled");
35793         }else{
35794             this.removeClass("x-tree-node-disabled");
35795         }
35796     },
35797
35798     onSelectedChange : function(state){
35799         if(state){
35800             this.focus();
35801             this.addClass("x-tree-selected");
35802         }else{
35803             //this.blur();
35804             this.removeClass("x-tree-selected");
35805         }
35806     },
35807
35808     onMove : function(tree, node, oldParent, newParent, index, refNode){
35809         this.childIndent = null;
35810         if(this.rendered){
35811             var targetNode = newParent.ui.getContainer();
35812             if(!targetNode){//target not rendered
35813                 this.holder = document.createElement("div");
35814                 this.holder.appendChild(this.wrap);
35815                 return;
35816             }
35817             var insertBefore = refNode ? refNode.ui.getEl() : null;
35818             if(insertBefore){
35819                 targetNode.insertBefore(this.wrap, insertBefore);
35820             }else{
35821                 targetNode.appendChild(this.wrap);
35822             }
35823             this.node.renderIndent(true);
35824         }
35825     },
35826
35827     addClass : function(cls){
35828         if(this.elNode){
35829             Roo.fly(this.elNode).addClass(cls);
35830         }
35831     },
35832
35833     removeClass : function(cls){
35834         if(this.elNode){
35835             Roo.fly(this.elNode).removeClass(cls);
35836         }
35837     },
35838
35839     remove : function(){
35840         if(this.rendered){
35841             this.holder = document.createElement("div");
35842             this.holder.appendChild(this.wrap);
35843         }
35844     },
35845
35846     fireEvent : function(){
35847         return this.node.fireEvent.apply(this.node, arguments);
35848     },
35849
35850     initEvents : function(){
35851         this.node.on("move", this.onMove, this);
35852         var E = Roo.EventManager;
35853         var a = this.anchor;
35854
35855         var el = Roo.fly(a, '_treeui');
35856
35857         if(Roo.isOpera){ // opera render bug ignores the CSS
35858             el.setStyle("text-decoration", "none");
35859         }
35860
35861         el.on("click", this.onClick, this);
35862         el.on("dblclick", this.onDblClick, this);
35863
35864         if(this.checkbox){
35865             Roo.EventManager.on(this.checkbox,
35866                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35867         }
35868
35869         el.on("contextmenu", this.onContextMenu, this);
35870
35871         var icon = Roo.fly(this.iconNode);
35872         icon.on("click", this.onClick, this);
35873         icon.on("dblclick", this.onDblClick, this);
35874         icon.on("contextmenu", this.onContextMenu, this);
35875         E.on(this.ecNode, "click", this.ecClick, this, true);
35876
35877         if(this.node.disabled){
35878             this.addClass("x-tree-node-disabled");
35879         }
35880         if(this.node.hidden){
35881             this.addClass("x-tree-node-disabled");
35882         }
35883         var ot = this.node.getOwnerTree();
35884         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35885         if(dd && (!this.node.isRoot || ot.rootVisible)){
35886             Roo.dd.Registry.register(this.elNode, {
35887                 node: this.node,
35888                 handles: this.getDDHandles(),
35889                 isHandle: false
35890             });
35891         }
35892     },
35893
35894     getDDHandles : function(){
35895         return [this.iconNode, this.textNode];
35896     },
35897
35898     hide : function(){
35899         if(this.rendered){
35900             this.wrap.style.display = "none";
35901         }
35902     },
35903
35904     show : function(){
35905         if(this.rendered){
35906             this.wrap.style.display = "";
35907         }
35908     },
35909
35910     onContextMenu : function(e){
35911         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35912             e.preventDefault();
35913             this.focus();
35914             this.fireEvent("contextmenu", this.node, e);
35915         }
35916     },
35917
35918     onClick : function(e){
35919         if(this.dropping){
35920             e.stopEvent();
35921             return;
35922         }
35923         if(this.fireEvent("beforeclick", this.node, e) !== false){
35924             if(!this.disabled && this.node.attributes.href){
35925                 this.fireEvent("click", this.node, e);
35926                 return;
35927             }
35928             e.preventDefault();
35929             if(this.disabled){
35930                 return;
35931             }
35932
35933             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35934                 this.node.toggle();
35935             }
35936
35937             this.fireEvent("click", this.node, e);
35938         }else{
35939             e.stopEvent();
35940         }
35941     },
35942
35943     onDblClick : function(e){
35944         e.preventDefault();
35945         if(this.disabled){
35946             return;
35947         }
35948         if(this.checkbox){
35949             this.toggleCheck();
35950         }
35951         if(!this.animating && this.node.hasChildNodes()){
35952             this.node.toggle();
35953         }
35954         this.fireEvent("dblclick", this.node, e);
35955     },
35956
35957     onCheckChange : function(){
35958         var checked = this.checkbox.checked;
35959         this.node.attributes.checked = checked;
35960         this.fireEvent('checkchange', this.node, checked);
35961     },
35962
35963     ecClick : function(e){
35964         if(!this.animating && this.node.hasChildNodes()){
35965             this.node.toggle();
35966         }
35967     },
35968
35969     startDrop : function(){
35970         this.dropping = true;
35971     },
35972
35973     // delayed drop so the click event doesn't get fired on a drop
35974     endDrop : function(){
35975        setTimeout(function(){
35976            this.dropping = false;
35977        }.createDelegate(this), 50);
35978     },
35979
35980     expand : function(){
35981         this.updateExpandIcon();
35982         this.ctNode.style.display = "";
35983     },
35984
35985     focus : function(){
35986         if(!this.node.preventHScroll){
35987             try{this.anchor.focus();
35988             }catch(e){}
35989         }else if(!Roo.isIE){
35990             try{
35991                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35992                 var l = noscroll.scrollLeft;
35993                 this.anchor.focus();
35994                 noscroll.scrollLeft = l;
35995             }catch(e){}
35996         }
35997     },
35998
35999     toggleCheck : function(value){
36000         var cb = this.checkbox;
36001         if(cb){
36002             cb.checked = (value === undefined ? !cb.checked : value);
36003         }
36004     },
36005
36006     blur : function(){
36007         try{
36008             this.anchor.blur();
36009         }catch(e){}
36010     },
36011
36012     animExpand : function(callback){
36013         var ct = Roo.get(this.ctNode);
36014         ct.stopFx();
36015         if(!this.node.hasChildNodes()){
36016             this.updateExpandIcon();
36017             this.ctNode.style.display = "";
36018             Roo.callback(callback);
36019             return;
36020         }
36021         this.animating = true;
36022         this.updateExpandIcon();
36023
36024         ct.slideIn('t', {
36025            callback : function(){
36026                this.animating = false;
36027                Roo.callback(callback);
36028             },
36029             scope: this,
36030             duration: this.node.ownerTree.duration || .25
36031         });
36032     },
36033
36034     highlight : function(){
36035         var tree = this.node.getOwnerTree();
36036         Roo.fly(this.wrap).highlight(
36037             tree.hlColor || "C3DAF9",
36038             {endColor: tree.hlBaseColor}
36039         );
36040     },
36041
36042     collapse : function(){
36043         this.updateExpandIcon();
36044         this.ctNode.style.display = "none";
36045     },
36046
36047     animCollapse : function(callback){
36048         var ct = Roo.get(this.ctNode);
36049         ct.enableDisplayMode('block');
36050         ct.stopFx();
36051
36052         this.animating = true;
36053         this.updateExpandIcon();
36054
36055         ct.slideOut('t', {
36056             callback : function(){
36057                this.animating = false;
36058                Roo.callback(callback);
36059             },
36060             scope: this,
36061             duration: this.node.ownerTree.duration || .25
36062         });
36063     },
36064
36065     getContainer : function(){
36066         return this.ctNode;
36067     },
36068
36069     getEl : function(){
36070         return this.wrap;
36071     },
36072
36073     appendDDGhost : function(ghostNode){
36074         ghostNode.appendChild(this.elNode.cloneNode(true));
36075     },
36076
36077     getDDRepairXY : function(){
36078         return Roo.lib.Dom.getXY(this.iconNode);
36079     },
36080
36081     onRender : function(){
36082         this.render();
36083     },
36084
36085     render : function(bulkRender){
36086         var n = this.node, a = n.attributes;
36087         var targetNode = n.parentNode ?
36088               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36089
36090         if(!this.rendered){
36091             this.rendered = true;
36092
36093             this.renderElements(n, a, targetNode, bulkRender);
36094
36095             if(a.qtip){
36096                if(this.textNode.setAttributeNS){
36097                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36098                    if(a.qtipTitle){
36099                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36100                    }
36101                }else{
36102                    this.textNode.setAttribute("ext:qtip", a.qtip);
36103                    if(a.qtipTitle){
36104                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36105                    }
36106                }
36107             }else if(a.qtipCfg){
36108                 a.qtipCfg.target = Roo.id(this.textNode);
36109                 Roo.QuickTips.register(a.qtipCfg);
36110             }
36111             this.initEvents();
36112             if(!this.node.expanded){
36113                 this.updateExpandIcon();
36114             }
36115         }else{
36116             if(bulkRender === true) {
36117                 targetNode.appendChild(this.wrap);
36118             }
36119         }
36120     },
36121
36122     renderElements : function(n, a, targetNode, bulkRender)
36123     {
36124         // add some indent caching, this helps performance when rendering a large tree
36125         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36126         var t = n.getOwnerTree();
36127         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36128         if (typeof(n.attributes.html) != 'undefined') {
36129             txt = n.attributes.html;
36130         }
36131         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36132         var cb = typeof a.checked == 'boolean';
36133         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36134         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36135             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36136             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36137             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36138             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36139             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36140              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36141                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36142             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36143             "</li>"];
36144
36145         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36146             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36147                                 n.nextSibling.ui.getEl(), buf.join(""));
36148         }else{
36149             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36150         }
36151
36152         this.elNode = this.wrap.childNodes[0];
36153         this.ctNode = this.wrap.childNodes[1];
36154         var cs = this.elNode.childNodes;
36155         this.indentNode = cs[0];
36156         this.ecNode = cs[1];
36157         this.iconNode = cs[2];
36158         var index = 3;
36159         if(cb){
36160             this.checkbox = cs[3];
36161             index++;
36162         }
36163         this.anchor = cs[index];
36164         this.textNode = cs[index].firstChild;
36165     },
36166
36167     getAnchor : function(){
36168         return this.anchor;
36169     },
36170
36171     getTextEl : function(){
36172         return this.textNode;
36173     },
36174
36175     getIconEl : function(){
36176         return this.iconNode;
36177     },
36178
36179     isChecked : function(){
36180         return this.checkbox ? this.checkbox.checked : false;
36181     },
36182
36183     updateExpandIcon : function(){
36184         if(this.rendered){
36185             var n = this.node, c1, c2;
36186             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36187             var hasChild = n.hasChildNodes();
36188             if(hasChild){
36189                 if(n.expanded){
36190                     cls += "-minus";
36191                     c1 = "x-tree-node-collapsed";
36192                     c2 = "x-tree-node-expanded";
36193                 }else{
36194                     cls += "-plus";
36195                     c1 = "x-tree-node-expanded";
36196                     c2 = "x-tree-node-collapsed";
36197                 }
36198                 if(this.wasLeaf){
36199                     this.removeClass("x-tree-node-leaf");
36200                     this.wasLeaf = false;
36201                 }
36202                 if(this.c1 != c1 || this.c2 != c2){
36203                     Roo.fly(this.elNode).replaceClass(c1, c2);
36204                     this.c1 = c1; this.c2 = c2;
36205                 }
36206             }else{
36207                 // this changes non-leafs into leafs if they have no children.
36208                 // it's not very rational behaviour..
36209                 
36210                 if(!this.wasLeaf && this.node.leaf){
36211                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36212                     delete this.c1;
36213                     delete this.c2;
36214                     this.wasLeaf = true;
36215                 }
36216             }
36217             var ecc = "x-tree-ec-icon "+cls;
36218             if(this.ecc != ecc){
36219                 this.ecNode.className = ecc;
36220                 this.ecc = ecc;
36221             }
36222         }
36223     },
36224
36225     getChildIndent : function(){
36226         if(!this.childIndent){
36227             var buf = [];
36228             var p = this.node;
36229             while(p){
36230                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36231                     if(!p.isLast()) {
36232                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36233                     } else {
36234                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36235                     }
36236                 }
36237                 p = p.parentNode;
36238             }
36239             this.childIndent = buf.join("");
36240         }
36241         return this.childIndent;
36242     },
36243
36244     renderIndent : function(){
36245         if(this.rendered){
36246             var indent = "";
36247             var p = this.node.parentNode;
36248             if(p){
36249                 indent = p.ui.getChildIndent();
36250             }
36251             if(this.indentMarkup != indent){ // don't rerender if not required
36252                 this.indentNode.innerHTML = indent;
36253                 this.indentMarkup = indent;
36254             }
36255             this.updateExpandIcon();
36256         }
36257     }
36258 };
36259
36260 Roo.tree.RootTreeNodeUI = function(){
36261     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36262 };
36263 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36264     render : function(){
36265         if(!this.rendered){
36266             var targetNode = this.node.ownerTree.innerCt.dom;
36267             this.node.expanded = true;
36268             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36269             this.wrap = this.ctNode = targetNode.firstChild;
36270         }
36271     },
36272     collapse : function(){
36273     },
36274     expand : function(){
36275     }
36276 });/*
36277  * Based on:
36278  * Ext JS Library 1.1.1
36279  * Copyright(c) 2006-2007, Ext JS, LLC.
36280  *
36281  * Originally Released Under LGPL - original licence link has changed is not relivant.
36282  *
36283  * Fork - LGPL
36284  * <script type="text/javascript">
36285  */
36286 /**
36287  * @class Roo.tree.TreeLoader
36288  * @extends Roo.util.Observable
36289  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36290  * nodes from a specified URL. The response must be a javascript Array definition
36291  * who's elements are node definition objects. eg:
36292  * <pre><code>
36293 {  success : true,
36294    data :      [
36295    
36296     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36297     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36298     ]
36299 }
36300
36301
36302 </code></pre>
36303  * <br><br>
36304  * The old style respose with just an array is still supported, but not recommended.
36305  * <br><br>
36306  *
36307  * A server request is sent, and child nodes are loaded only when a node is expanded.
36308  * The loading node's id is passed to the server under the parameter name "node" to
36309  * enable the server to produce the correct child nodes.
36310  * <br><br>
36311  * To pass extra parameters, an event handler may be attached to the "beforeload"
36312  * event, and the parameters specified in the TreeLoader's baseParams property:
36313  * <pre><code>
36314     myTreeLoader.on("beforeload", function(treeLoader, node) {
36315         this.baseParams.category = node.attributes.category;
36316     }, this);
36317     
36318 </code></pre>
36319  *
36320  * This would pass an HTTP parameter called "category" to the server containing
36321  * the value of the Node's "category" attribute.
36322  * @constructor
36323  * Creates a new Treeloader.
36324  * @param {Object} config A config object containing config properties.
36325  */
36326 Roo.tree.TreeLoader = function(config){
36327     this.baseParams = {};
36328     this.requestMethod = "POST";
36329     Roo.apply(this, config);
36330
36331     this.addEvents({
36332     
36333         /**
36334          * @event beforeload
36335          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36336          * @param {Object} This TreeLoader object.
36337          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36338          * @param {Object} callback The callback function specified in the {@link #load} call.
36339          */
36340         beforeload : true,
36341         /**
36342          * @event load
36343          * Fires when the node has been successfuly loaded.
36344          * @param {Object} This TreeLoader object.
36345          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36346          * @param {Object} response The response object containing the data from the server.
36347          */
36348         load : true,
36349         /**
36350          * @event loadexception
36351          * Fires if the network request failed.
36352          * @param {Object} This TreeLoader object.
36353          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36354          * @param {Object} response The response object containing the data from the server.
36355          */
36356         loadexception : true,
36357         /**
36358          * @event create
36359          * Fires before a node is created, enabling you to return custom Node types 
36360          * @param {Object} This TreeLoader object.
36361          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36362          */
36363         create : true
36364     });
36365
36366     Roo.tree.TreeLoader.superclass.constructor.call(this);
36367 };
36368
36369 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36370     /**
36371     * @cfg {String} dataUrl The URL from which to request a Json string which
36372     * specifies an array of node definition object representing the child nodes
36373     * to be loaded.
36374     */
36375     /**
36376     * @cfg {String} requestMethod either GET or POST
36377     * defaults to POST (due to BC)
36378     * to be loaded.
36379     */
36380     /**
36381     * @cfg {Object} baseParams (optional) An object containing properties which
36382     * specify HTTP parameters to be passed to each request for child nodes.
36383     */
36384     /**
36385     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36386     * created by this loader. If the attributes sent by the server have an attribute in this object,
36387     * they take priority.
36388     */
36389     /**
36390     * @cfg {Object} uiProviders (optional) An object containing properties which
36391     * 
36392     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36393     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36394     * <i>uiProvider</i> attribute of a returned child node is a string rather
36395     * than a reference to a TreeNodeUI implementation, this that string value
36396     * is used as a property name in the uiProviders object. You can define the provider named
36397     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36398     */
36399     uiProviders : {},
36400
36401     /**
36402     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36403     * child nodes before loading.
36404     */
36405     clearOnLoad : true,
36406
36407     /**
36408     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36409     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36410     * Grid query { data : [ .....] }
36411     */
36412     
36413     root : false,
36414      /**
36415     * @cfg {String} queryParam (optional) 
36416     * Name of the query as it will be passed on the querystring (defaults to 'node')
36417     * eg. the request will be ?node=[id]
36418     */
36419     
36420     
36421     queryParam: false,
36422     
36423     /**
36424      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36425      * This is called automatically when a node is expanded, but may be used to reload
36426      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36427      * @param {Roo.tree.TreeNode} node
36428      * @param {Function} callback
36429      */
36430     load : function(node, callback){
36431         if(this.clearOnLoad){
36432             while(node.firstChild){
36433                 node.removeChild(node.firstChild);
36434             }
36435         }
36436         if(node.attributes.children){ // preloaded json children
36437             var cs = node.attributes.children;
36438             for(var i = 0, len = cs.length; i < len; i++){
36439                 node.appendChild(this.createNode(cs[i]));
36440             }
36441             if(typeof callback == "function"){
36442                 callback();
36443             }
36444         }else if(this.dataUrl){
36445             this.requestData(node, callback);
36446         }
36447     },
36448
36449     getParams: function(node){
36450         var buf = [], bp = this.baseParams;
36451         for(var key in bp){
36452             if(typeof bp[key] != "function"){
36453                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36454             }
36455         }
36456         var n = this.queryParam === false ? 'node' : this.queryParam;
36457         buf.push(n + "=", encodeURIComponent(node.id));
36458         return buf.join("");
36459     },
36460
36461     requestData : function(node, callback){
36462         if(this.fireEvent("beforeload", this, node, callback) !== false){
36463             this.transId = Roo.Ajax.request({
36464                 method:this.requestMethod,
36465                 url: this.dataUrl||this.url,
36466                 success: this.handleResponse,
36467                 failure: this.handleFailure,
36468                 scope: this,
36469                 argument: {callback: callback, node: node},
36470                 params: this.getParams(node)
36471             });
36472         }else{
36473             // if the load is cancelled, make sure we notify
36474             // the node that we are done
36475             if(typeof callback == "function"){
36476                 callback();
36477             }
36478         }
36479     },
36480
36481     isLoading : function(){
36482         return this.transId ? true : false;
36483     },
36484
36485     abort : function(){
36486         if(this.isLoading()){
36487             Roo.Ajax.abort(this.transId);
36488         }
36489     },
36490
36491     // private
36492     createNode : function(attr)
36493     {
36494         // apply baseAttrs, nice idea Corey!
36495         if(this.baseAttrs){
36496             Roo.applyIf(attr, this.baseAttrs);
36497         }
36498         if(this.applyLoader !== false){
36499             attr.loader = this;
36500         }
36501         // uiProvider = depreciated..
36502         
36503         if(typeof(attr.uiProvider) == 'string'){
36504            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36505                 /**  eval:var:attr */ eval(attr.uiProvider);
36506         }
36507         if(typeof(this.uiProviders['default']) != 'undefined') {
36508             attr.uiProvider = this.uiProviders['default'];
36509         }
36510         
36511         this.fireEvent('create', this, attr);
36512         
36513         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36514         return(attr.leaf ?
36515                         new Roo.tree.TreeNode(attr) :
36516                         new Roo.tree.AsyncTreeNode(attr));
36517     },
36518
36519     processResponse : function(response, node, callback)
36520     {
36521         var json = response.responseText;
36522         try {
36523             
36524             var o = Roo.decode(json);
36525             
36526             if (this.root === false && typeof(o.success) != undefined) {
36527                 this.root = 'data'; // the default behaviour for list like data..
36528                 }
36529                 
36530             if (this.root !== false &&  !o.success) {
36531                 // it's a failure condition.
36532                 var a = response.argument;
36533                 this.fireEvent("loadexception", this, a.node, response);
36534                 Roo.log("Load failed - should have a handler really");
36535                 return;
36536             }
36537             
36538             
36539             
36540             if (this.root !== false) {
36541                  o = o[this.root];
36542             }
36543             
36544             for(var i = 0, len = o.length; i < len; i++){
36545                 var n = this.createNode(o[i]);
36546                 if(n){
36547                     node.appendChild(n);
36548                 }
36549             }
36550             if(typeof callback == "function"){
36551                 callback(this, node);
36552             }
36553         }catch(e){
36554             this.handleFailure(response);
36555         }
36556     },
36557
36558     handleResponse : function(response){
36559         this.transId = false;
36560         var a = response.argument;
36561         this.processResponse(response, a.node, a.callback);
36562         this.fireEvent("load", this, a.node, response);
36563     },
36564
36565     handleFailure : function(response)
36566     {
36567         // should handle failure better..
36568         this.transId = false;
36569         var a = response.argument;
36570         this.fireEvent("loadexception", this, a.node, response);
36571         if(typeof a.callback == "function"){
36572             a.callback(this, a.node);
36573         }
36574     }
36575 });/*
36576  * Based on:
36577  * Ext JS Library 1.1.1
36578  * Copyright(c) 2006-2007, Ext JS, LLC.
36579  *
36580  * Originally Released Under LGPL - original licence link has changed is not relivant.
36581  *
36582  * Fork - LGPL
36583  * <script type="text/javascript">
36584  */
36585
36586 /**
36587 * @class Roo.tree.TreeFilter
36588 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36589 * @param {TreePanel} tree
36590 * @param {Object} config (optional)
36591  */
36592 Roo.tree.TreeFilter = function(tree, config){
36593     this.tree = tree;
36594     this.filtered = {};
36595     Roo.apply(this, config);
36596 };
36597
36598 Roo.tree.TreeFilter.prototype = {
36599     clearBlank:false,
36600     reverse:false,
36601     autoClear:false,
36602     remove:false,
36603
36604      /**
36605      * Filter the data by a specific attribute.
36606      * @param {String/RegExp} value Either string that the attribute value
36607      * should start with or a RegExp to test against the attribute
36608      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36609      * @param {TreeNode} startNode (optional) The node to start the filter at.
36610      */
36611     filter : function(value, attr, startNode){
36612         attr = attr || "text";
36613         var f;
36614         if(typeof value == "string"){
36615             var vlen = value.length;
36616             // auto clear empty filter
36617             if(vlen == 0 && this.clearBlank){
36618                 this.clear();
36619                 return;
36620             }
36621             value = value.toLowerCase();
36622             f = function(n){
36623                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36624             };
36625         }else if(value.exec){ // regex?
36626             f = function(n){
36627                 return value.test(n.attributes[attr]);
36628             };
36629         }else{
36630             throw 'Illegal filter type, must be string or regex';
36631         }
36632         this.filterBy(f, null, startNode);
36633         },
36634
36635     /**
36636      * Filter by a function. The passed function will be called with each
36637      * node in the tree (or from the startNode). If the function returns true, the node is kept
36638      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36639      * @param {Function} fn The filter function
36640      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36641      */
36642     filterBy : function(fn, scope, startNode){
36643         startNode = startNode || this.tree.root;
36644         if(this.autoClear){
36645             this.clear();
36646         }
36647         var af = this.filtered, rv = this.reverse;
36648         var f = function(n){
36649             if(n == startNode){
36650                 return true;
36651             }
36652             if(af[n.id]){
36653                 return false;
36654             }
36655             var m = fn.call(scope || n, n);
36656             if(!m || rv){
36657                 af[n.id] = n;
36658                 n.ui.hide();
36659                 return false;
36660             }
36661             return true;
36662         };
36663         startNode.cascade(f);
36664         if(this.remove){
36665            for(var id in af){
36666                if(typeof id != "function"){
36667                    var n = af[id];
36668                    if(n && n.parentNode){
36669                        n.parentNode.removeChild(n);
36670                    }
36671                }
36672            }
36673         }
36674     },
36675
36676     /**
36677      * Clears the current filter. Note: with the "remove" option
36678      * set a filter cannot be cleared.
36679      */
36680     clear : function(){
36681         var t = this.tree;
36682         var af = this.filtered;
36683         for(var id in af){
36684             if(typeof id != "function"){
36685                 var n = af[id];
36686                 if(n){
36687                     n.ui.show();
36688                 }
36689             }
36690         }
36691         this.filtered = {};
36692     }
36693 };
36694 /*
36695  * Based on:
36696  * Ext JS Library 1.1.1
36697  * Copyright(c) 2006-2007, Ext JS, LLC.
36698  *
36699  * Originally Released Under LGPL - original licence link has changed is not relivant.
36700  *
36701  * Fork - LGPL
36702  * <script type="text/javascript">
36703  */
36704  
36705
36706 /**
36707  * @class Roo.tree.TreeSorter
36708  * Provides sorting of nodes in a TreePanel
36709  * 
36710  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36711  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36712  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36713  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36714  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36715  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36716  * @constructor
36717  * @param {TreePanel} tree
36718  * @param {Object} config
36719  */
36720 Roo.tree.TreeSorter = function(tree, config){
36721     Roo.apply(this, config);
36722     tree.on("beforechildrenrendered", this.doSort, this);
36723     tree.on("append", this.updateSort, this);
36724     tree.on("insert", this.updateSort, this);
36725     
36726     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36727     var p = this.property || "text";
36728     var sortType = this.sortType;
36729     var fs = this.folderSort;
36730     var cs = this.caseSensitive === true;
36731     var leafAttr = this.leafAttr || 'leaf';
36732
36733     this.sortFn = function(n1, n2){
36734         if(fs){
36735             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36736                 return 1;
36737             }
36738             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36739                 return -1;
36740             }
36741         }
36742         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36743         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36744         if(v1 < v2){
36745                         return dsc ? +1 : -1;
36746                 }else if(v1 > v2){
36747                         return dsc ? -1 : +1;
36748         }else{
36749                 return 0;
36750         }
36751     };
36752 };
36753
36754 Roo.tree.TreeSorter.prototype = {
36755     doSort : function(node){
36756         node.sort(this.sortFn);
36757     },
36758     
36759     compareNodes : function(n1, n2){
36760         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36761     },
36762     
36763     updateSort : function(tree, node){
36764         if(node.childrenRendered){
36765             this.doSort.defer(1, this, [node]);
36766         }
36767     }
36768 };/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778
36779 if(Roo.dd.DropZone){
36780     
36781 Roo.tree.TreeDropZone = function(tree, config){
36782     this.allowParentInsert = false;
36783     this.allowContainerDrop = false;
36784     this.appendOnly = false;
36785     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36786     this.tree = tree;
36787     this.lastInsertClass = "x-tree-no-status";
36788     this.dragOverData = {};
36789 };
36790
36791 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36792     ddGroup : "TreeDD",
36793     scroll:  true,
36794     
36795     expandDelay : 1000,
36796     
36797     expandNode : function(node){
36798         if(node.hasChildNodes() && !node.isExpanded()){
36799             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36800         }
36801     },
36802     
36803     queueExpand : function(node){
36804         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36805     },
36806     
36807     cancelExpand : function(){
36808         if(this.expandProcId){
36809             clearTimeout(this.expandProcId);
36810             this.expandProcId = false;
36811         }
36812     },
36813     
36814     isValidDropPoint : function(n, pt, dd, e, data){
36815         if(!n || !data){ return false; }
36816         var targetNode = n.node;
36817         var dropNode = data.node;
36818         // default drop rules
36819         if(!(targetNode && targetNode.isTarget && pt)){
36820             return false;
36821         }
36822         if(pt == "append" && targetNode.allowChildren === false){
36823             return false;
36824         }
36825         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36826             return false;
36827         }
36828         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36829             return false;
36830         }
36831         // reuse the object
36832         var overEvent = this.dragOverData;
36833         overEvent.tree = this.tree;
36834         overEvent.target = targetNode;
36835         overEvent.data = data;
36836         overEvent.point = pt;
36837         overEvent.source = dd;
36838         overEvent.rawEvent = e;
36839         overEvent.dropNode = dropNode;
36840         overEvent.cancel = false;  
36841         var result = this.tree.fireEvent("nodedragover", overEvent);
36842         return overEvent.cancel === false && result !== false;
36843     },
36844     
36845     getDropPoint : function(e, n, dd)
36846     {
36847         var tn = n.node;
36848         if(tn.isRoot){
36849             return tn.allowChildren !== false ? "append" : false; // always append for root
36850         }
36851         var dragEl = n.ddel;
36852         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36853         var y = Roo.lib.Event.getPageY(e);
36854         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36855         
36856         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36857         var noAppend = tn.allowChildren === false;
36858         if(this.appendOnly || tn.parentNode.allowChildren === false){
36859             return noAppend ? false : "append";
36860         }
36861         var noBelow = false;
36862         if(!this.allowParentInsert){
36863             noBelow = tn.hasChildNodes() && tn.isExpanded();
36864         }
36865         var q = (b - t) / (noAppend ? 2 : 3);
36866         if(y >= t && y < (t + q)){
36867             return "above";
36868         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36869             return "below";
36870         }else{
36871             return "append";
36872         }
36873     },
36874     
36875     onNodeEnter : function(n, dd, e, data)
36876     {
36877         this.cancelExpand();
36878     },
36879     
36880     onNodeOver : function(n, dd, e, data)
36881     {
36882        
36883         var pt = this.getDropPoint(e, n, dd);
36884         var node = n.node;
36885         
36886         // auto node expand check
36887         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36888             this.queueExpand(node);
36889         }else if(pt != "append"){
36890             this.cancelExpand();
36891         }
36892         
36893         // set the insert point style on the target node
36894         var returnCls = this.dropNotAllowed;
36895         if(this.isValidDropPoint(n, pt, dd, e, data)){
36896            if(pt){
36897                var el = n.ddel;
36898                var cls;
36899                if(pt == "above"){
36900                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36901                    cls = "x-tree-drag-insert-above";
36902                }else if(pt == "below"){
36903                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36904                    cls = "x-tree-drag-insert-below";
36905                }else{
36906                    returnCls = "x-tree-drop-ok-append";
36907                    cls = "x-tree-drag-append";
36908                }
36909                if(this.lastInsertClass != cls){
36910                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36911                    this.lastInsertClass = cls;
36912                }
36913            }
36914        }
36915        return returnCls;
36916     },
36917     
36918     onNodeOut : function(n, dd, e, data){
36919         
36920         this.cancelExpand();
36921         this.removeDropIndicators(n);
36922     },
36923     
36924     onNodeDrop : function(n, dd, e, data){
36925         var point = this.getDropPoint(e, n, dd);
36926         var targetNode = n.node;
36927         targetNode.ui.startDrop();
36928         if(!this.isValidDropPoint(n, point, dd, e, data)){
36929             targetNode.ui.endDrop();
36930             return false;
36931         }
36932         // first try to find the drop node
36933         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36934         var dropEvent = {
36935             tree : this.tree,
36936             target: targetNode,
36937             data: data,
36938             point: point,
36939             source: dd,
36940             rawEvent: e,
36941             dropNode: dropNode,
36942             cancel: !dropNode   
36943         };
36944         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36945         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36946             targetNode.ui.endDrop();
36947             return false;
36948         }
36949         // allow target changing
36950         targetNode = dropEvent.target;
36951         if(point == "append" && !targetNode.isExpanded()){
36952             targetNode.expand(false, null, function(){
36953                 this.completeDrop(dropEvent);
36954             }.createDelegate(this));
36955         }else{
36956             this.completeDrop(dropEvent);
36957         }
36958         return true;
36959     },
36960     
36961     completeDrop : function(de){
36962         var ns = de.dropNode, p = de.point, t = de.target;
36963         if(!(ns instanceof Array)){
36964             ns = [ns];
36965         }
36966         var n;
36967         for(var i = 0, len = ns.length; i < len; i++){
36968             n = ns[i];
36969             if(p == "above"){
36970                 t.parentNode.insertBefore(n, t);
36971             }else if(p == "below"){
36972                 t.parentNode.insertBefore(n, t.nextSibling);
36973             }else{
36974                 t.appendChild(n);
36975             }
36976         }
36977         n.ui.focus();
36978         if(this.tree.hlDrop){
36979             n.ui.highlight();
36980         }
36981         t.ui.endDrop();
36982         this.tree.fireEvent("nodedrop", de);
36983     },
36984     
36985     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36986         if(this.tree.hlDrop){
36987             dropNode.ui.focus();
36988             dropNode.ui.highlight();
36989         }
36990         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36991     },
36992     
36993     getTree : function(){
36994         return this.tree;
36995     },
36996     
36997     removeDropIndicators : function(n){
36998         if(n && n.ddel){
36999             var el = n.ddel;
37000             Roo.fly(el).removeClass([
37001                     "x-tree-drag-insert-above",
37002                     "x-tree-drag-insert-below",
37003                     "x-tree-drag-append"]);
37004             this.lastInsertClass = "_noclass";
37005         }
37006     },
37007     
37008     beforeDragDrop : function(target, e, id){
37009         this.cancelExpand();
37010         return true;
37011     },
37012     
37013     afterRepair : function(data){
37014         if(data && Roo.enableFx){
37015             data.node.ui.highlight();
37016         }
37017         this.hideProxy();
37018     } 
37019     
37020 });
37021
37022 }
37023 /*
37024  * Based on:
37025  * Ext JS Library 1.1.1
37026  * Copyright(c) 2006-2007, Ext JS, LLC.
37027  *
37028  * Originally Released Under LGPL - original licence link has changed is not relivant.
37029  *
37030  * Fork - LGPL
37031  * <script type="text/javascript">
37032  */
37033  
37034
37035 if(Roo.dd.DragZone){
37036 Roo.tree.TreeDragZone = function(tree, config){
37037     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37038     this.tree = tree;
37039 };
37040
37041 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37042     ddGroup : "TreeDD",
37043    
37044     onBeforeDrag : function(data, e){
37045         var n = data.node;
37046         return n && n.draggable && !n.disabled;
37047     },
37048      
37049     
37050     onInitDrag : function(e){
37051         var data = this.dragData;
37052         this.tree.getSelectionModel().select(data.node);
37053         this.proxy.update("");
37054         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37055         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37056     },
37057     
37058     getRepairXY : function(e, data){
37059         return data.node.ui.getDDRepairXY();
37060     },
37061     
37062     onEndDrag : function(data, e){
37063         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37064         
37065         
37066     },
37067     
37068     onValidDrop : function(dd, e, id){
37069         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37070         this.hideProxy();
37071     },
37072     
37073     beforeInvalidDrop : function(e, id){
37074         // this scrolls the original position back into view
37075         var sm = this.tree.getSelectionModel();
37076         sm.clearSelections();
37077         sm.select(this.dragData.node);
37078     }
37079 });
37080 }/*
37081  * Based on:
37082  * Ext JS Library 1.1.1
37083  * Copyright(c) 2006-2007, Ext JS, LLC.
37084  *
37085  * Originally Released Under LGPL - original licence link has changed is not relivant.
37086  *
37087  * Fork - LGPL
37088  * <script type="text/javascript">
37089  */
37090 /**
37091  * @class Roo.tree.TreeEditor
37092  * @extends Roo.Editor
37093  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37094  * as the editor field.
37095  * @constructor
37096  * @param {Object} config (used to be the tree panel.)
37097  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37098  * 
37099  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37100  * @cfg {Roo.form.TextField|Object} field The field configuration
37101  *
37102  * 
37103  */
37104 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37105     var tree = config;
37106     var field;
37107     if (oldconfig) { // old style..
37108         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37109     } else {
37110         // new style..
37111         tree = config.tree;
37112         config.field = config.field  || {};
37113         config.field.xtype = 'TextField';
37114         field = Roo.factory(config.field, Roo.form);
37115     }
37116     config = config || {};
37117     
37118     
37119     this.addEvents({
37120         /**
37121          * @event beforenodeedit
37122          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37123          * false from the handler of this event.
37124          * @param {Editor} this
37125          * @param {Roo.tree.Node} node 
37126          */
37127         "beforenodeedit" : true
37128     });
37129     
37130     //Roo.log(config);
37131     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37132
37133     this.tree = tree;
37134
37135     tree.on('beforeclick', this.beforeNodeClick, this);
37136     tree.getTreeEl().on('mousedown', this.hide, this);
37137     this.on('complete', this.updateNode, this);
37138     this.on('beforestartedit', this.fitToTree, this);
37139     this.on('startedit', this.bindScroll, this, {delay:10});
37140     this.on('specialkey', this.onSpecialKey, this);
37141 };
37142
37143 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37144     /**
37145      * @cfg {String} alignment
37146      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37147      */
37148     alignment: "l-l",
37149     // inherit
37150     autoSize: false,
37151     /**
37152      * @cfg {Boolean} hideEl
37153      * True to hide the bound element while the editor is displayed (defaults to false)
37154      */
37155     hideEl : false,
37156     /**
37157      * @cfg {String} cls
37158      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37159      */
37160     cls: "x-small-editor x-tree-editor",
37161     /**
37162      * @cfg {Boolean} shim
37163      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37164      */
37165     shim:false,
37166     // inherit
37167     shadow:"frame",
37168     /**
37169      * @cfg {Number} maxWidth
37170      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37171      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37172      * scroll and client offsets into account prior to each edit.
37173      */
37174     maxWidth: 250,
37175
37176     editDelay : 350,
37177
37178     // private
37179     fitToTree : function(ed, el){
37180         var td = this.tree.getTreeEl().dom, nd = el.dom;
37181         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37182             td.scrollLeft = nd.offsetLeft;
37183         }
37184         var w = Math.min(
37185                 this.maxWidth,
37186                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37187         this.setSize(w, '');
37188         
37189         return this.fireEvent('beforenodeedit', this, this.editNode);
37190         
37191     },
37192
37193     // private
37194     triggerEdit : function(node){
37195         this.completeEdit();
37196         this.editNode = node;
37197         this.startEdit(node.ui.textNode, node.text);
37198     },
37199
37200     // private
37201     bindScroll : function(){
37202         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37203     },
37204
37205     // private
37206     beforeNodeClick : function(node, e){
37207         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37208         this.lastClick = new Date();
37209         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37210             e.stopEvent();
37211             this.triggerEdit(node);
37212             return false;
37213         }
37214         return true;
37215     },
37216
37217     // private
37218     updateNode : function(ed, value){
37219         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37220         this.editNode.setText(value);
37221     },
37222
37223     // private
37224     onHide : function(){
37225         Roo.tree.TreeEditor.superclass.onHide.call(this);
37226         if(this.editNode){
37227             this.editNode.ui.focus();
37228         }
37229     },
37230
37231     // private
37232     onSpecialKey : function(field, e){
37233         var k = e.getKey();
37234         if(k == e.ESC){
37235             e.stopEvent();
37236             this.cancelEdit();
37237         }else if(k == e.ENTER && !e.hasModifier()){
37238             e.stopEvent();
37239             this.completeEdit();
37240         }
37241     }
37242 });//<Script type="text/javascript">
37243 /*
37244  * Based on:
37245  * Ext JS Library 1.1.1
37246  * Copyright(c) 2006-2007, Ext JS, LLC.
37247  *
37248  * Originally Released Under LGPL - original licence link has changed is not relivant.
37249  *
37250  * Fork - LGPL
37251  * <script type="text/javascript">
37252  */
37253  
37254 /**
37255  * Not documented??? - probably should be...
37256  */
37257
37258 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37259     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37260     
37261     renderElements : function(n, a, targetNode, bulkRender){
37262         //consel.log("renderElements?");
37263         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37264
37265         var t = n.getOwnerTree();
37266         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37267         
37268         var cols = t.columns;
37269         var bw = t.borderWidth;
37270         var c = cols[0];
37271         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37272          var cb = typeof a.checked == "boolean";
37273         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37274         var colcls = 'x-t-' + tid + '-c0';
37275         var buf = [
37276             '<li class="x-tree-node">',
37277             
37278                 
37279                 '<div class="x-tree-node-el ', a.cls,'">',
37280                     // extran...
37281                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37282                 
37283                 
37284                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37285                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37286                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37287                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37288                            (a.iconCls ? ' '+a.iconCls : ''),
37289                            '" unselectable="on" />',
37290                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37291                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37292                              
37293                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37294                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37295                             '<span unselectable="on" qtip="' + tx + '">',
37296                              tx,
37297                              '</span></a>' ,
37298                     '</div>',
37299                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37300                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37301                  ];
37302         for(var i = 1, len = cols.length; i < len; i++){
37303             c = cols[i];
37304             colcls = 'x-t-' + tid + '-c' +i;
37305             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37306             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37307                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37308                       "</div>");
37309          }
37310          
37311          buf.push(
37312             '</a>',
37313             '<div class="x-clear"></div></div>',
37314             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37315             "</li>");
37316         
37317         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37318             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37319                                 n.nextSibling.ui.getEl(), buf.join(""));
37320         }else{
37321             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37322         }
37323         var el = this.wrap.firstChild;
37324         this.elRow = el;
37325         this.elNode = el.firstChild;
37326         this.ranchor = el.childNodes[1];
37327         this.ctNode = this.wrap.childNodes[1];
37328         var cs = el.firstChild.childNodes;
37329         this.indentNode = cs[0];
37330         this.ecNode = cs[1];
37331         this.iconNode = cs[2];
37332         var index = 3;
37333         if(cb){
37334             this.checkbox = cs[3];
37335             index++;
37336         }
37337         this.anchor = cs[index];
37338         
37339         this.textNode = cs[index].firstChild;
37340         
37341         //el.on("click", this.onClick, this);
37342         //el.on("dblclick", this.onDblClick, this);
37343         
37344         
37345        // console.log(this);
37346     },
37347     initEvents : function(){
37348         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37349         
37350             
37351         var a = this.ranchor;
37352
37353         var el = Roo.get(a);
37354
37355         if(Roo.isOpera){ // opera render bug ignores the CSS
37356             el.setStyle("text-decoration", "none");
37357         }
37358
37359         el.on("click", this.onClick, this);
37360         el.on("dblclick", this.onDblClick, this);
37361         el.on("contextmenu", this.onContextMenu, this);
37362         
37363     },
37364     
37365     /*onSelectedChange : function(state){
37366         if(state){
37367             this.focus();
37368             this.addClass("x-tree-selected");
37369         }else{
37370             //this.blur();
37371             this.removeClass("x-tree-selected");
37372         }
37373     },*/
37374     addClass : function(cls){
37375         if(this.elRow){
37376             Roo.fly(this.elRow).addClass(cls);
37377         }
37378         
37379     },
37380     
37381     
37382     removeClass : function(cls){
37383         if(this.elRow){
37384             Roo.fly(this.elRow).removeClass(cls);
37385         }
37386     }
37387
37388     
37389     
37390 });//<Script type="text/javascript">
37391
37392 /*
37393  * Based on:
37394  * Ext JS Library 1.1.1
37395  * Copyright(c) 2006-2007, Ext JS, LLC.
37396  *
37397  * Originally Released Under LGPL - original licence link has changed is not relivant.
37398  *
37399  * Fork - LGPL
37400  * <script type="text/javascript">
37401  */
37402  
37403
37404 /**
37405  * @class Roo.tree.ColumnTree
37406  * @extends Roo.data.TreePanel
37407  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37408  * @cfg {int} borderWidth  compined right/left border allowance
37409  * @constructor
37410  * @param {String/HTMLElement/Element} el The container element
37411  * @param {Object} config
37412  */
37413 Roo.tree.ColumnTree =  function(el, config)
37414 {
37415    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37416    this.addEvents({
37417         /**
37418         * @event resize
37419         * Fire this event on a container when it resizes
37420         * @param {int} w Width
37421         * @param {int} h Height
37422         */
37423        "resize" : true
37424     });
37425     this.on('resize', this.onResize, this);
37426 };
37427
37428 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37429     //lines:false,
37430     
37431     
37432     borderWidth: Roo.isBorderBox ? 0 : 2, 
37433     headEls : false,
37434     
37435     render : function(){
37436         // add the header.....
37437        
37438         Roo.tree.ColumnTree.superclass.render.apply(this);
37439         
37440         this.el.addClass('x-column-tree');
37441         
37442         this.headers = this.el.createChild(
37443             {cls:'x-tree-headers'},this.innerCt.dom);
37444    
37445         var cols = this.columns, c;
37446         var totalWidth = 0;
37447         this.headEls = [];
37448         var  len = cols.length;
37449         for(var i = 0; i < len; i++){
37450              c = cols[i];
37451              totalWidth += c.width;
37452             this.headEls.push(this.headers.createChild({
37453                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37454                  cn: {
37455                      cls:'x-tree-hd-text',
37456                      html: c.header
37457                  },
37458                  style:'width:'+(c.width-this.borderWidth)+'px;'
37459              }));
37460         }
37461         this.headers.createChild({cls:'x-clear'});
37462         // prevent floats from wrapping when clipped
37463         this.headers.setWidth(totalWidth);
37464         //this.innerCt.setWidth(totalWidth);
37465         this.innerCt.setStyle({ overflow: 'auto' });
37466         this.onResize(this.width, this.height);
37467              
37468         
37469     },
37470     onResize : function(w,h)
37471     {
37472         this.height = h;
37473         this.width = w;
37474         // resize cols..
37475         this.innerCt.setWidth(this.width);
37476         this.innerCt.setHeight(this.height-20);
37477         
37478         // headers...
37479         var cols = this.columns, c;
37480         var totalWidth = 0;
37481         var expEl = false;
37482         var len = cols.length;
37483         for(var i = 0; i < len; i++){
37484             c = cols[i];
37485             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37486                 // it's the expander..
37487                 expEl  = this.headEls[i];
37488                 continue;
37489             }
37490             totalWidth += c.width;
37491             
37492         }
37493         if (expEl) {
37494             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37495         }
37496         this.headers.setWidth(w-20);
37497
37498         
37499         
37500         
37501     }
37502 });
37503 /*
37504  * Based on:
37505  * Ext JS Library 1.1.1
37506  * Copyright(c) 2006-2007, Ext JS, LLC.
37507  *
37508  * Originally Released Under LGPL - original licence link has changed is not relivant.
37509  *
37510  * Fork - LGPL
37511  * <script type="text/javascript">
37512  */
37513  
37514 /**
37515  * @class Roo.menu.Menu
37516  * @extends Roo.util.Observable
37517  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37518  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37519  * @constructor
37520  * Creates a new Menu
37521  * @param {Object} config Configuration options
37522  */
37523 Roo.menu.Menu = function(config){
37524     
37525     Roo.menu.Menu.superclass.constructor.call(this, config);
37526     
37527     this.id = this.id || Roo.id();
37528     this.addEvents({
37529         /**
37530          * @event beforeshow
37531          * Fires before this menu is displayed
37532          * @param {Roo.menu.Menu} this
37533          */
37534         beforeshow : true,
37535         /**
37536          * @event beforehide
37537          * Fires before this menu is hidden
37538          * @param {Roo.menu.Menu} this
37539          */
37540         beforehide : true,
37541         /**
37542          * @event show
37543          * Fires after this menu is displayed
37544          * @param {Roo.menu.Menu} this
37545          */
37546         show : true,
37547         /**
37548          * @event hide
37549          * Fires after this menu is hidden
37550          * @param {Roo.menu.Menu} this
37551          */
37552         hide : true,
37553         /**
37554          * @event click
37555          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37556          * @param {Roo.menu.Menu} this
37557          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37558          * @param {Roo.EventObject} e
37559          */
37560         click : true,
37561         /**
37562          * @event mouseover
37563          * Fires when the mouse is hovering over this menu
37564          * @param {Roo.menu.Menu} this
37565          * @param {Roo.EventObject} e
37566          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37567          */
37568         mouseover : true,
37569         /**
37570          * @event mouseout
37571          * Fires when the mouse exits this menu
37572          * @param {Roo.menu.Menu} this
37573          * @param {Roo.EventObject} e
37574          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37575          */
37576         mouseout : true,
37577         /**
37578          * @event itemclick
37579          * Fires when a menu item contained in this menu is clicked
37580          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37581          * @param {Roo.EventObject} e
37582          */
37583         itemclick: true
37584     });
37585     if (this.registerMenu) {
37586         Roo.menu.MenuMgr.register(this);
37587     }
37588     
37589     var mis = this.items;
37590     this.items = new Roo.util.MixedCollection();
37591     if(mis){
37592         this.add.apply(this, mis);
37593     }
37594 };
37595
37596 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37597     /**
37598      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37599      */
37600     minWidth : 120,
37601     /**
37602      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37603      * for bottom-right shadow (defaults to "sides")
37604      */
37605     shadow : "sides",
37606     /**
37607      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37608      * this menu (defaults to "tl-tr?")
37609      */
37610     subMenuAlign : "tl-tr?",
37611     /**
37612      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37613      * relative to its element of origin (defaults to "tl-bl?")
37614      */
37615     defaultAlign : "tl-bl?",
37616     /**
37617      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37618      */
37619     allowOtherMenus : false,
37620     /**
37621      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37622      */
37623     registerMenu : true,
37624
37625     hidden:true,
37626
37627     // private
37628     render : function(){
37629         if(this.el){
37630             return;
37631         }
37632         var el = this.el = new Roo.Layer({
37633             cls: "x-menu",
37634             shadow:this.shadow,
37635             constrain: false,
37636             parentEl: this.parentEl || document.body,
37637             zindex:15000
37638         });
37639
37640         this.keyNav = new Roo.menu.MenuNav(this);
37641
37642         if(this.plain){
37643             el.addClass("x-menu-plain");
37644         }
37645         if(this.cls){
37646             el.addClass(this.cls);
37647         }
37648         // generic focus element
37649         this.focusEl = el.createChild({
37650             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37651         });
37652         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37653         //disabling touch- as it's causing issues ..
37654         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37655         ul.on('click'   , this.onClick, this);
37656         
37657         
37658         ul.on("mouseover", this.onMouseOver, this);
37659         ul.on("mouseout", this.onMouseOut, this);
37660         this.items.each(function(item){
37661             if (item.hidden) {
37662                 return;
37663             }
37664             
37665             var li = document.createElement("li");
37666             li.className = "x-menu-list-item";
37667             ul.dom.appendChild(li);
37668             item.render(li, this);
37669         }, this);
37670         this.ul = ul;
37671         this.autoWidth();
37672     },
37673
37674     // private
37675     autoWidth : function(){
37676         var el = this.el, ul = this.ul;
37677         if(!el){
37678             return;
37679         }
37680         var w = this.width;
37681         if(w){
37682             el.setWidth(w);
37683         }else if(Roo.isIE){
37684             el.setWidth(this.minWidth);
37685             var t = el.dom.offsetWidth; // force recalc
37686             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37687         }
37688     },
37689
37690     // private
37691     delayAutoWidth : function(){
37692         if(this.rendered){
37693             if(!this.awTask){
37694                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37695             }
37696             this.awTask.delay(20);
37697         }
37698     },
37699
37700     // private
37701     findTargetItem : function(e){
37702         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37703         if(t && t.menuItemId){
37704             return this.items.get(t.menuItemId);
37705         }
37706     },
37707
37708     // private
37709     onClick : function(e){
37710         Roo.log("menu.onClick");
37711         var t = this.findTargetItem(e);
37712         if(!t){
37713             return;
37714         }
37715         Roo.log(e);
37716         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37717             if(t == this.activeItem && t.shouldDeactivate(e)){
37718                 this.activeItem.deactivate();
37719                 delete this.activeItem;
37720                 return;
37721             }
37722             if(t.canActivate){
37723                 this.setActiveItem(t, true);
37724             }
37725             return;
37726             
37727             
37728         }
37729         
37730         t.onClick(e);
37731         this.fireEvent("click", this, t, e);
37732     },
37733
37734     // private
37735     setActiveItem : function(item, autoExpand){
37736         if(item != this.activeItem){
37737             if(this.activeItem){
37738                 this.activeItem.deactivate();
37739             }
37740             this.activeItem = item;
37741             item.activate(autoExpand);
37742         }else if(autoExpand){
37743             item.expandMenu();
37744         }
37745     },
37746
37747     // private
37748     tryActivate : function(start, step){
37749         var items = this.items;
37750         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37751             var item = items.get(i);
37752             if(!item.disabled && item.canActivate){
37753                 this.setActiveItem(item, false);
37754                 return item;
37755             }
37756         }
37757         return false;
37758     },
37759
37760     // private
37761     onMouseOver : function(e){
37762         var t;
37763         if(t = this.findTargetItem(e)){
37764             if(t.canActivate && !t.disabled){
37765                 this.setActiveItem(t, true);
37766             }
37767         }
37768         this.fireEvent("mouseover", this, e, t);
37769     },
37770
37771     // private
37772     onMouseOut : function(e){
37773         var t;
37774         if(t = this.findTargetItem(e)){
37775             if(t == this.activeItem && t.shouldDeactivate(e)){
37776                 this.activeItem.deactivate();
37777                 delete this.activeItem;
37778             }
37779         }
37780         this.fireEvent("mouseout", this, e, t);
37781     },
37782
37783     /**
37784      * Read-only.  Returns true if the menu is currently displayed, else false.
37785      * @type Boolean
37786      */
37787     isVisible : function(){
37788         return this.el && !this.hidden;
37789     },
37790
37791     /**
37792      * Displays this menu relative to another element
37793      * @param {String/HTMLElement/Roo.Element} element The element to align to
37794      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37795      * the element (defaults to this.defaultAlign)
37796      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37797      */
37798     show : function(el, pos, parentMenu){
37799         this.parentMenu = parentMenu;
37800         if(!this.el){
37801             this.render();
37802         }
37803         this.fireEvent("beforeshow", this);
37804         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37805     },
37806
37807     /**
37808      * Displays this menu at a specific xy position
37809      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37810      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37811      */
37812     showAt : function(xy, parentMenu, /* private: */_e){
37813         this.parentMenu = parentMenu;
37814         if(!this.el){
37815             this.render();
37816         }
37817         if(_e !== false){
37818             this.fireEvent("beforeshow", this);
37819             xy = this.el.adjustForConstraints(xy);
37820         }
37821         this.el.setXY(xy);
37822         this.el.show();
37823         this.hidden = false;
37824         this.focus();
37825         this.fireEvent("show", this);
37826     },
37827
37828     focus : function(){
37829         if(!this.hidden){
37830             this.doFocus.defer(50, this);
37831         }
37832     },
37833
37834     doFocus : function(){
37835         if(!this.hidden){
37836             this.focusEl.focus();
37837         }
37838     },
37839
37840     /**
37841      * Hides this menu and optionally all parent menus
37842      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37843      */
37844     hide : function(deep){
37845         if(this.el && this.isVisible()){
37846             this.fireEvent("beforehide", this);
37847             if(this.activeItem){
37848                 this.activeItem.deactivate();
37849                 this.activeItem = null;
37850             }
37851             this.el.hide();
37852             this.hidden = true;
37853             this.fireEvent("hide", this);
37854         }
37855         if(deep === true && this.parentMenu){
37856             this.parentMenu.hide(true);
37857         }
37858     },
37859
37860     /**
37861      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37862      * Any of the following are valid:
37863      * <ul>
37864      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37865      * <li>An HTMLElement object which will be converted to a menu item</li>
37866      * <li>A menu item config object that will be created as a new menu item</li>
37867      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37868      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37869      * </ul>
37870      * Usage:
37871      * <pre><code>
37872 // Create the menu
37873 var menu = new Roo.menu.Menu();
37874
37875 // Create a menu item to add by reference
37876 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37877
37878 // Add a bunch of items at once using different methods.
37879 // Only the last item added will be returned.
37880 var item = menu.add(
37881     menuItem,                // add existing item by ref
37882     'Dynamic Item',          // new TextItem
37883     '-',                     // new separator
37884     { text: 'Config Item' }  // new item by config
37885 );
37886 </code></pre>
37887      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37888      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37889      */
37890     add : function(){
37891         var a = arguments, l = a.length, item;
37892         for(var i = 0; i < l; i++){
37893             var el = a[i];
37894             if ((typeof(el) == "object") && el.xtype && el.xns) {
37895                 el = Roo.factory(el, Roo.menu);
37896             }
37897             
37898             if(el.render){ // some kind of Item
37899                 item = this.addItem(el);
37900             }else if(typeof el == "string"){ // string
37901                 if(el == "separator" || el == "-"){
37902                     item = this.addSeparator();
37903                 }else{
37904                     item = this.addText(el);
37905                 }
37906             }else if(el.tagName || el.el){ // element
37907                 item = this.addElement(el);
37908             }else if(typeof el == "object"){ // must be menu item config?
37909                 item = this.addMenuItem(el);
37910             }
37911         }
37912         return item;
37913     },
37914
37915     /**
37916      * Returns this menu's underlying {@link Roo.Element} object
37917      * @return {Roo.Element} The element
37918      */
37919     getEl : function(){
37920         if(!this.el){
37921             this.render();
37922         }
37923         return this.el;
37924     },
37925
37926     /**
37927      * Adds a separator bar to the menu
37928      * @return {Roo.menu.Item} The menu item that was added
37929      */
37930     addSeparator : function(){
37931         return this.addItem(new Roo.menu.Separator());
37932     },
37933
37934     /**
37935      * Adds an {@link Roo.Element} object to the menu
37936      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37937      * @return {Roo.menu.Item} The menu item that was added
37938      */
37939     addElement : function(el){
37940         return this.addItem(new Roo.menu.BaseItem(el));
37941     },
37942
37943     /**
37944      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37945      * @param {Roo.menu.Item} item The menu item to add
37946      * @return {Roo.menu.Item} The menu item that was added
37947      */
37948     addItem : function(item){
37949         this.items.add(item);
37950         if(this.ul){
37951             var li = document.createElement("li");
37952             li.className = "x-menu-list-item";
37953             this.ul.dom.appendChild(li);
37954             item.render(li, this);
37955             this.delayAutoWidth();
37956         }
37957         return item;
37958     },
37959
37960     /**
37961      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37962      * @param {Object} config A MenuItem config object
37963      * @return {Roo.menu.Item} The menu item that was added
37964      */
37965     addMenuItem : function(config){
37966         if(!(config instanceof Roo.menu.Item)){
37967             if(typeof config.checked == "boolean"){ // must be check menu item config?
37968                 config = new Roo.menu.CheckItem(config);
37969             }else{
37970                 config = new Roo.menu.Item(config);
37971             }
37972         }
37973         return this.addItem(config);
37974     },
37975
37976     /**
37977      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37978      * @param {String} text The text to display in the menu item
37979      * @return {Roo.menu.Item} The menu item that was added
37980      */
37981     addText : function(text){
37982         return this.addItem(new Roo.menu.TextItem({ text : text }));
37983     },
37984
37985     /**
37986      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37987      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37988      * @param {Roo.menu.Item} item The menu item to add
37989      * @return {Roo.menu.Item} The menu item that was added
37990      */
37991     insert : function(index, item){
37992         this.items.insert(index, item);
37993         if(this.ul){
37994             var li = document.createElement("li");
37995             li.className = "x-menu-list-item";
37996             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37997             item.render(li, this);
37998             this.delayAutoWidth();
37999         }
38000         return item;
38001     },
38002
38003     /**
38004      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38005      * @param {Roo.menu.Item} item The menu item to remove
38006      */
38007     remove : function(item){
38008         this.items.removeKey(item.id);
38009         item.destroy();
38010     },
38011
38012     /**
38013      * Removes and destroys all items in the menu
38014      */
38015     removeAll : function(){
38016         var f;
38017         while(f = this.items.first()){
38018             this.remove(f);
38019         }
38020     }
38021 });
38022
38023 // MenuNav is a private utility class used internally by the Menu
38024 Roo.menu.MenuNav = function(menu){
38025     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38026     this.scope = this.menu = menu;
38027 };
38028
38029 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38030     doRelay : function(e, h){
38031         var k = e.getKey();
38032         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38033             this.menu.tryActivate(0, 1);
38034             return false;
38035         }
38036         return h.call(this.scope || this, e, this.menu);
38037     },
38038
38039     up : function(e, m){
38040         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38041             m.tryActivate(m.items.length-1, -1);
38042         }
38043     },
38044
38045     down : function(e, m){
38046         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38047             m.tryActivate(0, 1);
38048         }
38049     },
38050
38051     right : function(e, m){
38052         if(m.activeItem){
38053             m.activeItem.expandMenu(true);
38054         }
38055     },
38056
38057     left : function(e, m){
38058         m.hide();
38059         if(m.parentMenu && m.parentMenu.activeItem){
38060             m.parentMenu.activeItem.activate();
38061         }
38062     },
38063
38064     enter : function(e, m){
38065         if(m.activeItem){
38066             e.stopPropagation();
38067             m.activeItem.onClick(e);
38068             m.fireEvent("click", this, m.activeItem);
38069             return true;
38070         }
38071     }
38072 });/*
38073  * Based on:
38074  * Ext JS Library 1.1.1
38075  * Copyright(c) 2006-2007, Ext JS, LLC.
38076  *
38077  * Originally Released Under LGPL - original licence link has changed is not relivant.
38078  *
38079  * Fork - LGPL
38080  * <script type="text/javascript">
38081  */
38082  
38083 /**
38084  * @class Roo.menu.MenuMgr
38085  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38086  * @singleton
38087  */
38088 Roo.menu.MenuMgr = function(){
38089    var menus, active, groups = {}, attached = false, lastShow = new Date();
38090
38091    // private - called when first menu is created
38092    function init(){
38093        menus = {};
38094        active = new Roo.util.MixedCollection();
38095        Roo.get(document).addKeyListener(27, function(){
38096            if(active.length > 0){
38097                hideAll();
38098            }
38099        });
38100    }
38101
38102    // private
38103    function hideAll(){
38104        if(active && active.length > 0){
38105            var c = active.clone();
38106            c.each(function(m){
38107                m.hide();
38108            });
38109        }
38110    }
38111
38112    // private
38113    function onHide(m){
38114        active.remove(m);
38115        if(active.length < 1){
38116            Roo.get(document).un("mousedown", onMouseDown);
38117            attached = false;
38118        }
38119    }
38120
38121    // private
38122    function onShow(m){
38123        var last = active.last();
38124        lastShow = new Date();
38125        active.add(m);
38126        if(!attached){
38127            Roo.get(document).on("mousedown", onMouseDown);
38128            attached = true;
38129        }
38130        if(m.parentMenu){
38131           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38132           m.parentMenu.activeChild = m;
38133        }else if(last && last.isVisible()){
38134           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38135        }
38136    }
38137
38138    // private
38139    function onBeforeHide(m){
38140        if(m.activeChild){
38141            m.activeChild.hide();
38142        }
38143        if(m.autoHideTimer){
38144            clearTimeout(m.autoHideTimer);
38145            delete m.autoHideTimer;
38146        }
38147    }
38148
38149    // private
38150    function onBeforeShow(m){
38151        var pm = m.parentMenu;
38152        if(!pm && !m.allowOtherMenus){
38153            hideAll();
38154        }else if(pm && pm.activeChild && active != m){
38155            pm.activeChild.hide();
38156        }
38157    }
38158
38159    // private
38160    function onMouseDown(e){
38161        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38162            hideAll();
38163        }
38164    }
38165
38166    // private
38167    function onBeforeCheck(mi, state){
38168        if(state){
38169            var g = groups[mi.group];
38170            for(var i = 0, l = g.length; i < l; i++){
38171                if(g[i] != mi){
38172                    g[i].setChecked(false);
38173                }
38174            }
38175        }
38176    }
38177
38178    return {
38179
38180        /**
38181         * Hides all menus that are currently visible
38182         */
38183        hideAll : function(){
38184             hideAll();  
38185        },
38186
38187        // private
38188        register : function(menu){
38189            if(!menus){
38190                init();
38191            }
38192            menus[menu.id] = menu;
38193            menu.on("beforehide", onBeforeHide);
38194            menu.on("hide", onHide);
38195            menu.on("beforeshow", onBeforeShow);
38196            menu.on("show", onShow);
38197            var g = menu.group;
38198            if(g && menu.events["checkchange"]){
38199                if(!groups[g]){
38200                    groups[g] = [];
38201                }
38202                groups[g].push(menu);
38203                menu.on("checkchange", onCheck);
38204            }
38205        },
38206
38207         /**
38208          * Returns a {@link Roo.menu.Menu} object
38209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38210          * be used to generate and return a new Menu instance.
38211          */
38212        get : function(menu){
38213            if(typeof menu == "string"){ // menu id
38214                return menus[menu];
38215            }else if(menu.events){  // menu instance
38216                return menu;
38217            }else if(typeof menu.length == 'number'){ // array of menu items?
38218                return new Roo.menu.Menu({items:menu});
38219            }else{ // otherwise, must be a config
38220                return new Roo.menu.Menu(menu);
38221            }
38222        },
38223
38224        // private
38225        unregister : function(menu){
38226            delete menus[menu.id];
38227            menu.un("beforehide", onBeforeHide);
38228            menu.un("hide", onHide);
38229            menu.un("beforeshow", onBeforeShow);
38230            menu.un("show", onShow);
38231            var g = menu.group;
38232            if(g && menu.events["checkchange"]){
38233                groups[g].remove(menu);
38234                menu.un("checkchange", onCheck);
38235            }
38236        },
38237
38238        // private
38239        registerCheckable : function(menuItem){
38240            var g = menuItem.group;
38241            if(g){
38242                if(!groups[g]){
38243                    groups[g] = [];
38244                }
38245                groups[g].push(menuItem);
38246                menuItem.on("beforecheckchange", onBeforeCheck);
38247            }
38248        },
38249
38250        // private
38251        unregisterCheckable : function(menuItem){
38252            var g = menuItem.group;
38253            if(g){
38254                groups[g].remove(menuItem);
38255                menuItem.un("beforecheckchange", onBeforeCheck);
38256            }
38257        }
38258    };
38259 }();/*
38260  * Based on:
38261  * Ext JS Library 1.1.1
38262  * Copyright(c) 2006-2007, Ext JS, LLC.
38263  *
38264  * Originally Released Under LGPL - original licence link has changed is not relivant.
38265  *
38266  * Fork - LGPL
38267  * <script type="text/javascript">
38268  */
38269  
38270
38271 /**
38272  * @class Roo.menu.BaseItem
38273  * @extends Roo.Component
38274  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38275  * management and base configuration options shared by all menu components.
38276  * @constructor
38277  * Creates a new BaseItem
38278  * @param {Object} config Configuration options
38279  */
38280 Roo.menu.BaseItem = function(config){
38281     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38282
38283     this.addEvents({
38284         /**
38285          * @event click
38286          * Fires when this item is clicked
38287          * @param {Roo.menu.BaseItem} this
38288          * @param {Roo.EventObject} e
38289          */
38290         click: true,
38291         /**
38292          * @event activate
38293          * Fires when this item is activated
38294          * @param {Roo.menu.BaseItem} this
38295          */
38296         activate : true,
38297         /**
38298          * @event deactivate
38299          * Fires when this item is deactivated
38300          * @param {Roo.menu.BaseItem} this
38301          */
38302         deactivate : true
38303     });
38304
38305     if(this.handler){
38306         this.on("click", this.handler, this.scope, true);
38307     }
38308 };
38309
38310 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38311     /**
38312      * @cfg {Function} handler
38313      * A function that will handle the click event of this menu item (defaults to undefined)
38314      */
38315     /**
38316      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38317      */
38318     canActivate : false,
38319     
38320      /**
38321      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38322      */
38323     hidden: false,
38324     
38325     /**
38326      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38327      */
38328     activeClass : "x-menu-item-active",
38329     /**
38330      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38331      */
38332     hideOnClick : true,
38333     /**
38334      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38335      */
38336     hideDelay : 100,
38337
38338     // private
38339     ctype: "Roo.menu.BaseItem",
38340
38341     // private
38342     actionMode : "container",
38343
38344     // private
38345     render : function(container, parentMenu){
38346         this.parentMenu = parentMenu;
38347         Roo.menu.BaseItem.superclass.render.call(this, container);
38348         this.container.menuItemId = this.id;
38349     },
38350
38351     // private
38352     onRender : function(container, position){
38353         this.el = Roo.get(this.el);
38354         container.dom.appendChild(this.el.dom);
38355     },
38356
38357     // private
38358     onClick : function(e){
38359         if(!this.disabled && this.fireEvent("click", this, e) !== false
38360                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38361             this.handleClick(e);
38362         }else{
38363             e.stopEvent();
38364         }
38365     },
38366
38367     // private
38368     activate : function(){
38369         if(this.disabled){
38370             return false;
38371         }
38372         var li = this.container;
38373         li.addClass(this.activeClass);
38374         this.region = li.getRegion().adjust(2, 2, -2, -2);
38375         this.fireEvent("activate", this);
38376         return true;
38377     },
38378
38379     // private
38380     deactivate : function(){
38381         this.container.removeClass(this.activeClass);
38382         this.fireEvent("deactivate", this);
38383     },
38384
38385     // private
38386     shouldDeactivate : function(e){
38387         return !this.region || !this.region.contains(e.getPoint());
38388     },
38389
38390     // private
38391     handleClick : function(e){
38392         if(this.hideOnClick){
38393             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38394         }
38395     },
38396
38397     // private
38398     expandMenu : function(autoActivate){
38399         // do nothing
38400     },
38401
38402     // private
38403     hideMenu : function(){
38404         // do nothing
38405     }
38406 });/*
38407  * Based on:
38408  * Ext JS Library 1.1.1
38409  * Copyright(c) 2006-2007, Ext JS, LLC.
38410  *
38411  * Originally Released Under LGPL - original licence link has changed is not relivant.
38412  *
38413  * Fork - LGPL
38414  * <script type="text/javascript">
38415  */
38416  
38417 /**
38418  * @class Roo.menu.Adapter
38419  * @extends Roo.menu.BaseItem
38420  * 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.
38421  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38422  * @constructor
38423  * Creates a new Adapter
38424  * @param {Object} config Configuration options
38425  */
38426 Roo.menu.Adapter = function(component, config){
38427     Roo.menu.Adapter.superclass.constructor.call(this, config);
38428     this.component = component;
38429 };
38430 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38431     // private
38432     canActivate : true,
38433
38434     // private
38435     onRender : function(container, position){
38436         this.component.render(container);
38437         this.el = this.component.getEl();
38438     },
38439
38440     // private
38441     activate : function(){
38442         if(this.disabled){
38443             return false;
38444         }
38445         this.component.focus();
38446         this.fireEvent("activate", this);
38447         return true;
38448     },
38449
38450     // private
38451     deactivate : function(){
38452         this.fireEvent("deactivate", this);
38453     },
38454
38455     // private
38456     disable : function(){
38457         this.component.disable();
38458         Roo.menu.Adapter.superclass.disable.call(this);
38459     },
38460
38461     // private
38462     enable : function(){
38463         this.component.enable();
38464         Roo.menu.Adapter.superclass.enable.call(this);
38465     }
38466 });/*
38467  * Based on:
38468  * Ext JS Library 1.1.1
38469  * Copyright(c) 2006-2007, Ext JS, LLC.
38470  *
38471  * Originally Released Under LGPL - original licence link has changed is not relivant.
38472  *
38473  * Fork - LGPL
38474  * <script type="text/javascript">
38475  */
38476
38477 /**
38478  * @class Roo.menu.TextItem
38479  * @extends Roo.menu.BaseItem
38480  * Adds a static text string to a menu, usually used as either a heading or group separator.
38481  * Note: old style constructor with text is still supported.
38482  * 
38483  * @constructor
38484  * Creates a new TextItem
38485  * @param {Object} cfg Configuration
38486  */
38487 Roo.menu.TextItem = function(cfg){
38488     if (typeof(cfg) == 'string') {
38489         this.text = cfg;
38490     } else {
38491         Roo.apply(this,cfg);
38492     }
38493     
38494     Roo.menu.TextItem.superclass.constructor.call(this);
38495 };
38496
38497 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38498     /**
38499      * @cfg {String} text Text to show on item.
38500      */
38501     text : '',
38502     
38503     /**
38504      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38505      */
38506     hideOnClick : false,
38507     /**
38508      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38509      */
38510     itemCls : "x-menu-text",
38511
38512     // private
38513     onRender : function(){
38514         var s = document.createElement("span");
38515         s.className = this.itemCls;
38516         s.innerHTML = this.text;
38517         this.el = s;
38518         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38519     }
38520 });/*
38521  * Based on:
38522  * Ext JS Library 1.1.1
38523  * Copyright(c) 2006-2007, Ext JS, LLC.
38524  *
38525  * Originally Released Under LGPL - original licence link has changed is not relivant.
38526  *
38527  * Fork - LGPL
38528  * <script type="text/javascript">
38529  */
38530
38531 /**
38532  * @class Roo.menu.Separator
38533  * @extends Roo.menu.BaseItem
38534  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38535  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38536  * @constructor
38537  * @param {Object} config Configuration options
38538  */
38539 Roo.menu.Separator = function(config){
38540     Roo.menu.Separator.superclass.constructor.call(this, config);
38541 };
38542
38543 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38544     /**
38545      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38546      */
38547     itemCls : "x-menu-sep",
38548     /**
38549      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38550      */
38551     hideOnClick : false,
38552
38553     // private
38554     onRender : function(li){
38555         var s = document.createElement("span");
38556         s.className = this.itemCls;
38557         s.innerHTML = "&#160;";
38558         this.el = s;
38559         li.addClass("x-menu-sep-li");
38560         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38561     }
38562 });/*
38563  * Based on:
38564  * Ext JS Library 1.1.1
38565  * Copyright(c) 2006-2007, Ext JS, LLC.
38566  *
38567  * Originally Released Under LGPL - original licence link has changed is not relivant.
38568  *
38569  * Fork - LGPL
38570  * <script type="text/javascript">
38571  */
38572 /**
38573  * @class Roo.menu.Item
38574  * @extends Roo.menu.BaseItem
38575  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38576  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38577  * activation and click handling.
38578  * @constructor
38579  * Creates a new Item
38580  * @param {Object} config Configuration options
38581  */
38582 Roo.menu.Item = function(config){
38583     Roo.menu.Item.superclass.constructor.call(this, config);
38584     if(this.menu){
38585         this.menu = Roo.menu.MenuMgr.get(this.menu);
38586     }
38587 };
38588 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38589     
38590     /**
38591      * @cfg {String} text
38592      * The text to show on the menu item.
38593      */
38594     text: '',
38595      /**
38596      * @cfg {String} HTML to render in menu
38597      * The text to show on the menu item (HTML version).
38598      */
38599     html: '',
38600     /**
38601      * @cfg {String} icon
38602      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38603      */
38604     icon: undefined,
38605     /**
38606      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38607      */
38608     itemCls : "x-menu-item",
38609     /**
38610      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38611      */
38612     canActivate : true,
38613     /**
38614      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38615      */
38616     showDelay: 200,
38617     // doc'd in BaseItem
38618     hideDelay: 200,
38619
38620     // private
38621     ctype: "Roo.menu.Item",
38622     
38623     // private
38624     onRender : function(container, position){
38625         var el = document.createElement("a");
38626         el.hideFocus = true;
38627         el.unselectable = "on";
38628         el.href = this.href || "#";
38629         if(this.hrefTarget){
38630             el.target = this.hrefTarget;
38631         }
38632         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38633         
38634         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38635         
38636         el.innerHTML = String.format(
38637                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38638                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38639         this.el = el;
38640         Roo.menu.Item.superclass.onRender.call(this, container, position);
38641     },
38642
38643     /**
38644      * Sets the text to display in this menu item
38645      * @param {String} text The text to display
38646      * @param {Boolean} isHTML true to indicate text is pure html.
38647      */
38648     setText : function(text, isHTML){
38649         if (isHTML) {
38650             this.html = text;
38651         } else {
38652             this.text = text;
38653             this.html = '';
38654         }
38655         if(this.rendered){
38656             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38657      
38658             this.el.update(String.format(
38659                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38660                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38661             this.parentMenu.autoWidth();
38662         }
38663     },
38664
38665     // private
38666     handleClick : function(e){
38667         if(!this.href){ // if no link defined, stop the event automatically
38668             e.stopEvent();
38669         }
38670         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38671     },
38672
38673     // private
38674     activate : function(autoExpand){
38675         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38676             this.focus();
38677             if(autoExpand){
38678                 this.expandMenu();
38679             }
38680         }
38681         return true;
38682     },
38683
38684     // private
38685     shouldDeactivate : function(e){
38686         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38687             if(this.menu && this.menu.isVisible()){
38688                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38689             }
38690             return true;
38691         }
38692         return false;
38693     },
38694
38695     // private
38696     deactivate : function(){
38697         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38698         this.hideMenu();
38699     },
38700
38701     // private
38702     expandMenu : function(autoActivate){
38703         if(!this.disabled && this.menu){
38704             clearTimeout(this.hideTimer);
38705             delete this.hideTimer;
38706             if(!this.menu.isVisible() && !this.showTimer){
38707                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38708             }else if (this.menu.isVisible() && autoActivate){
38709                 this.menu.tryActivate(0, 1);
38710             }
38711         }
38712     },
38713
38714     // private
38715     deferExpand : function(autoActivate){
38716         delete this.showTimer;
38717         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38718         if(autoActivate){
38719             this.menu.tryActivate(0, 1);
38720         }
38721     },
38722
38723     // private
38724     hideMenu : function(){
38725         clearTimeout(this.showTimer);
38726         delete this.showTimer;
38727         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38728             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38729         }
38730     },
38731
38732     // private
38733     deferHide : function(){
38734         delete this.hideTimer;
38735         this.menu.hide();
38736     }
38737 });/*
38738  * Based on:
38739  * Ext JS Library 1.1.1
38740  * Copyright(c) 2006-2007, Ext JS, LLC.
38741  *
38742  * Originally Released Under LGPL - original licence link has changed is not relivant.
38743  *
38744  * Fork - LGPL
38745  * <script type="text/javascript">
38746  */
38747  
38748 /**
38749  * @class Roo.menu.CheckItem
38750  * @extends Roo.menu.Item
38751  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38752  * @constructor
38753  * Creates a new CheckItem
38754  * @param {Object} config Configuration options
38755  */
38756 Roo.menu.CheckItem = function(config){
38757     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38758     this.addEvents({
38759         /**
38760          * @event beforecheckchange
38761          * Fires before the checked value is set, providing an opportunity to cancel if needed
38762          * @param {Roo.menu.CheckItem} this
38763          * @param {Boolean} checked The new checked value that will be set
38764          */
38765         "beforecheckchange" : true,
38766         /**
38767          * @event checkchange
38768          * Fires after the checked value has been set
38769          * @param {Roo.menu.CheckItem} this
38770          * @param {Boolean} checked The checked value that was set
38771          */
38772         "checkchange" : true
38773     });
38774     if(this.checkHandler){
38775         this.on('checkchange', this.checkHandler, this.scope);
38776     }
38777 };
38778 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38779     /**
38780      * @cfg {String} group
38781      * All check items with the same group name will automatically be grouped into a single-select
38782      * radio button group (defaults to '')
38783      */
38784     /**
38785      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38786      */
38787     itemCls : "x-menu-item x-menu-check-item",
38788     /**
38789      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38790      */
38791     groupClass : "x-menu-group-item",
38792
38793     /**
38794      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38795      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38796      * initialized with checked = true will be rendered as checked.
38797      */
38798     checked: false,
38799
38800     // private
38801     ctype: "Roo.menu.CheckItem",
38802
38803     // private
38804     onRender : function(c){
38805         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38806         if(this.group){
38807             this.el.addClass(this.groupClass);
38808         }
38809         Roo.menu.MenuMgr.registerCheckable(this);
38810         if(this.checked){
38811             this.checked = false;
38812             this.setChecked(true, true);
38813         }
38814     },
38815
38816     // private
38817     destroy : function(){
38818         if(this.rendered){
38819             Roo.menu.MenuMgr.unregisterCheckable(this);
38820         }
38821         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38822     },
38823
38824     /**
38825      * Set the checked state of this item
38826      * @param {Boolean} checked The new checked value
38827      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38828      */
38829     setChecked : function(state, suppressEvent){
38830         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38831             if(this.container){
38832                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38833             }
38834             this.checked = state;
38835             if(suppressEvent !== true){
38836                 this.fireEvent("checkchange", this, state);
38837             }
38838         }
38839     },
38840
38841     // private
38842     handleClick : function(e){
38843        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38844            this.setChecked(!this.checked);
38845        }
38846        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38847     }
38848 });/*
38849  * Based on:
38850  * Ext JS Library 1.1.1
38851  * Copyright(c) 2006-2007, Ext JS, LLC.
38852  *
38853  * Originally Released Under LGPL - original licence link has changed is not relivant.
38854  *
38855  * Fork - LGPL
38856  * <script type="text/javascript">
38857  */
38858  
38859 /**
38860  * @class Roo.menu.DateItem
38861  * @extends Roo.menu.Adapter
38862  * A menu item that wraps the {@link Roo.DatPicker} component.
38863  * @constructor
38864  * Creates a new DateItem
38865  * @param {Object} config Configuration options
38866  */
38867 Roo.menu.DateItem = function(config){
38868     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38869     /** The Roo.DatePicker object @type Roo.DatePicker */
38870     this.picker = this.component;
38871     this.addEvents({select: true});
38872     
38873     this.picker.on("render", function(picker){
38874         picker.getEl().swallowEvent("click");
38875         picker.container.addClass("x-menu-date-item");
38876     });
38877
38878     this.picker.on("select", this.onSelect, this);
38879 };
38880
38881 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38882     // private
38883     onSelect : function(picker, date){
38884         this.fireEvent("select", this, date, picker);
38885         Roo.menu.DateItem.superclass.handleClick.call(this);
38886     }
38887 });/*
38888  * Based on:
38889  * Ext JS Library 1.1.1
38890  * Copyright(c) 2006-2007, Ext JS, LLC.
38891  *
38892  * Originally Released Under LGPL - original licence link has changed is not relivant.
38893  *
38894  * Fork - LGPL
38895  * <script type="text/javascript">
38896  */
38897  
38898 /**
38899  * @class Roo.menu.ColorItem
38900  * @extends Roo.menu.Adapter
38901  * A menu item that wraps the {@link Roo.ColorPalette} component.
38902  * @constructor
38903  * Creates a new ColorItem
38904  * @param {Object} config Configuration options
38905  */
38906 Roo.menu.ColorItem = function(config){
38907     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38908     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38909     this.palette = this.component;
38910     this.relayEvents(this.palette, ["select"]);
38911     if(this.selectHandler){
38912         this.on('select', this.selectHandler, this.scope);
38913     }
38914 };
38915 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38916  * Based on:
38917  * Ext JS Library 1.1.1
38918  * Copyright(c) 2006-2007, Ext JS, LLC.
38919  *
38920  * Originally Released Under LGPL - original licence link has changed is not relivant.
38921  *
38922  * Fork - LGPL
38923  * <script type="text/javascript">
38924  */
38925  
38926
38927 /**
38928  * @class Roo.menu.DateMenu
38929  * @extends Roo.menu.Menu
38930  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38931  * @constructor
38932  * Creates a new DateMenu
38933  * @param {Object} config Configuration options
38934  */
38935 Roo.menu.DateMenu = function(config){
38936     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38937     this.plain = true;
38938     var di = new Roo.menu.DateItem(config);
38939     this.add(di);
38940     /**
38941      * The {@link Roo.DatePicker} instance for this DateMenu
38942      * @type DatePicker
38943      */
38944     this.picker = di.picker;
38945     /**
38946      * @event select
38947      * @param {DatePicker} picker
38948      * @param {Date} date
38949      */
38950     this.relayEvents(di, ["select"]);
38951     this.on('beforeshow', function(){
38952         if(this.picker){
38953             this.picker.hideMonthPicker(false);
38954         }
38955     }, this);
38956 };
38957 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38958     cls:'x-date-menu'
38959 });/*
38960  * Based on:
38961  * Ext JS Library 1.1.1
38962  * Copyright(c) 2006-2007, Ext JS, LLC.
38963  *
38964  * Originally Released Under LGPL - original licence link has changed is not relivant.
38965  *
38966  * Fork - LGPL
38967  * <script type="text/javascript">
38968  */
38969  
38970
38971 /**
38972  * @class Roo.menu.ColorMenu
38973  * @extends Roo.menu.Menu
38974  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38975  * @constructor
38976  * Creates a new ColorMenu
38977  * @param {Object} config Configuration options
38978  */
38979 Roo.menu.ColorMenu = function(config){
38980     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38981     this.plain = true;
38982     var ci = new Roo.menu.ColorItem(config);
38983     this.add(ci);
38984     /**
38985      * The {@link Roo.ColorPalette} instance for this ColorMenu
38986      * @type ColorPalette
38987      */
38988     this.palette = ci.palette;
38989     /**
38990      * @event select
38991      * @param {ColorPalette} palette
38992      * @param {String} color
38993      */
38994     this.relayEvents(ci, ["select"]);
38995 };
38996 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38997  * Based on:
38998  * Ext JS Library 1.1.1
38999  * Copyright(c) 2006-2007, Ext JS, LLC.
39000  *
39001  * Originally Released Under LGPL - original licence link has changed is not relivant.
39002  *
39003  * Fork - LGPL
39004  * <script type="text/javascript">
39005  */
39006  
39007 /**
39008  * @class Roo.form.TextItem
39009  * @extends Roo.BoxComponent
39010  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39011  * @constructor
39012  * Creates a new TextItem
39013  * @param {Object} config Configuration options
39014  */
39015 Roo.form.TextItem = function(config){
39016     Roo.form.TextItem.superclass.constructor.call(this, config);
39017 };
39018
39019 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39020     
39021     /**
39022      * @cfg {String} tag the tag for this item (default div)
39023      */
39024     tag : 'div',
39025     /**
39026      * @cfg {String} html the content for this item
39027      */
39028     html : '',
39029     
39030     getAutoCreate : function()
39031     {
39032         var cfg = {
39033             id: this.id,
39034             tag: this.tag,
39035             html: this.html,
39036             cls: 'x-form-item'
39037         };
39038         
39039         return cfg;
39040         
39041     },
39042     
39043     onRender : function(ct, position)
39044     {
39045         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39046         
39047         if(!this.el){
39048             var cfg = this.getAutoCreate();
39049             if(!cfg.name){
39050                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39051             }
39052             if (!cfg.name.length) {
39053                 delete cfg.name;
39054             }
39055             this.el = ct.createChild(cfg, position);
39056         }
39057     },
39058     /*
39059      * setHTML
39060      * @param {String} html update the Contents of the element.
39061      */
39062     setHTML : function(html)
39063     {
39064         this.fieldEl.dom.innerHTML = html;
39065     }
39066     
39067 });/*
39068  * Based on:
39069  * Ext JS Library 1.1.1
39070  * Copyright(c) 2006-2007, Ext JS, LLC.
39071  *
39072  * Originally Released Under LGPL - original licence link has changed is not relivant.
39073  *
39074  * Fork - LGPL
39075  * <script type="text/javascript">
39076  */
39077  
39078 /**
39079  * @class Roo.form.Field
39080  * @extends Roo.BoxComponent
39081  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39082  * @constructor
39083  * Creates a new Field
39084  * @param {Object} config Configuration options
39085  */
39086 Roo.form.Field = function(config){
39087     Roo.form.Field.superclass.constructor.call(this, config);
39088 };
39089
39090 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39091     /**
39092      * @cfg {String} fieldLabel Label to use when rendering a form.
39093      */
39094        /**
39095      * @cfg {String} qtip Mouse over tip
39096      */
39097      
39098     /**
39099      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39100      */
39101     invalidClass : "x-form-invalid",
39102     /**
39103      * @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")
39104      */
39105     invalidText : "The value in this field is invalid",
39106     /**
39107      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39108      */
39109     focusClass : "x-form-focus",
39110     /**
39111      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39112       automatic validation (defaults to "keyup").
39113      */
39114     validationEvent : "keyup",
39115     /**
39116      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39117      */
39118     validateOnBlur : true,
39119     /**
39120      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39121      */
39122     validationDelay : 250,
39123     /**
39124      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39125      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39126      */
39127     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39128     /**
39129      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39130      */
39131     fieldClass : "x-form-field",
39132     /**
39133      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39134      *<pre>
39135 Value         Description
39136 -----------   ----------------------------------------------------------------------
39137 qtip          Display a quick tip when the user hovers over the field
39138 title         Display a default browser title attribute popup
39139 under         Add a block div beneath the field containing the error text
39140 side          Add an error icon to the right of the field with a popup on hover
39141 [element id]  Add the error text directly to the innerHTML of the specified element
39142 </pre>
39143      */
39144     msgTarget : 'qtip',
39145     /**
39146      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39147      */
39148     msgFx : 'normal',
39149
39150     /**
39151      * @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.
39152      */
39153     readOnly : false,
39154
39155     /**
39156      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39157      */
39158     disabled : false,
39159
39160     /**
39161      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39162      */
39163     inputType : undefined,
39164     
39165     /**
39166      * @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).
39167          */
39168         tabIndex : undefined,
39169         
39170     // private
39171     isFormField : true,
39172
39173     // private
39174     hasFocus : false,
39175     /**
39176      * @property {Roo.Element} fieldEl
39177      * Element Containing the rendered Field (with label etc.)
39178      */
39179     /**
39180      * @cfg {Mixed} value A value to initialize this field with.
39181      */
39182     value : undefined,
39183
39184     /**
39185      * @cfg {String} name The field's HTML name attribute.
39186      */
39187     /**
39188      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39189      */
39190     // private
39191     loadedValue : false,
39192      
39193      
39194         // private ??
39195         initComponent : function(){
39196         Roo.form.Field.superclass.initComponent.call(this);
39197         this.addEvents({
39198             /**
39199              * @event focus
39200              * Fires when this field receives input focus.
39201              * @param {Roo.form.Field} this
39202              */
39203             focus : true,
39204             /**
39205              * @event blur
39206              * Fires when this field loses input focus.
39207              * @param {Roo.form.Field} this
39208              */
39209             blur : true,
39210             /**
39211              * @event specialkey
39212              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39213              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39214              * @param {Roo.form.Field} this
39215              * @param {Roo.EventObject} e The event object
39216              */
39217             specialkey : true,
39218             /**
39219              * @event change
39220              * Fires just before the field blurs if the field value has changed.
39221              * @param {Roo.form.Field} this
39222              * @param {Mixed} newValue The new value
39223              * @param {Mixed} oldValue The original value
39224              */
39225             change : true,
39226             /**
39227              * @event invalid
39228              * Fires after the field has been marked as invalid.
39229              * @param {Roo.form.Field} this
39230              * @param {String} msg The validation message
39231              */
39232             invalid : true,
39233             /**
39234              * @event valid
39235              * Fires after the field has been validated with no errors.
39236              * @param {Roo.form.Field} this
39237              */
39238             valid : true,
39239              /**
39240              * @event keyup
39241              * Fires after the key up
39242              * @param {Roo.form.Field} this
39243              * @param {Roo.EventObject}  e The event Object
39244              */
39245             keyup : true
39246         });
39247     },
39248
39249     /**
39250      * Returns the name attribute of the field if available
39251      * @return {String} name The field name
39252      */
39253     getName: function(){
39254          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39255     },
39256
39257     // private
39258     onRender : function(ct, position){
39259         Roo.form.Field.superclass.onRender.call(this, ct, position);
39260         if(!this.el){
39261             var cfg = this.getAutoCreate();
39262             if(!cfg.name){
39263                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39264             }
39265             if (!cfg.name.length) {
39266                 delete cfg.name;
39267             }
39268             if(this.inputType){
39269                 cfg.type = this.inputType;
39270             }
39271             this.el = ct.createChild(cfg, position);
39272         }
39273         var type = this.el.dom.type;
39274         if(type){
39275             if(type == 'password'){
39276                 type = 'text';
39277             }
39278             this.el.addClass('x-form-'+type);
39279         }
39280         if(this.readOnly){
39281             this.el.dom.readOnly = true;
39282         }
39283         if(this.tabIndex !== undefined){
39284             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39285         }
39286
39287         this.el.addClass([this.fieldClass, this.cls]);
39288         this.initValue();
39289     },
39290
39291     /**
39292      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39293      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39294      * @return {Roo.form.Field} this
39295      */
39296     applyTo : function(target){
39297         this.allowDomMove = false;
39298         this.el = Roo.get(target);
39299         this.render(this.el.dom.parentNode);
39300         return this;
39301     },
39302
39303     // private
39304     initValue : function(){
39305         if(this.value !== undefined){
39306             this.setValue(this.value);
39307         }else if(this.el.dom.value.length > 0){
39308             this.setValue(this.el.dom.value);
39309         }
39310     },
39311
39312     /**
39313      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39314      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39315      */
39316     isDirty : function() {
39317         if(this.disabled) {
39318             return false;
39319         }
39320         return String(this.getValue()) !== String(this.originalValue);
39321     },
39322
39323     /**
39324      * stores the current value in loadedValue
39325      */
39326     resetHasChanged : function()
39327     {
39328         this.loadedValue = String(this.getValue());
39329     },
39330     /**
39331      * checks the current value against the 'loaded' value.
39332      * Note - will return false if 'resetHasChanged' has not been called first.
39333      */
39334     hasChanged : function()
39335     {
39336         if(this.disabled || this.readOnly) {
39337             return false;
39338         }
39339         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39340     },
39341     
39342     
39343     
39344     // private
39345     afterRender : function(){
39346         Roo.form.Field.superclass.afterRender.call(this);
39347         this.initEvents();
39348     },
39349
39350     // private
39351     fireKey : function(e){
39352         //Roo.log('field ' + e.getKey());
39353         if(e.isNavKeyPress()){
39354             this.fireEvent("specialkey", this, e);
39355         }
39356     },
39357
39358     /**
39359      * Resets the current field value to the originally loaded value and clears any validation messages
39360      */
39361     reset : function(){
39362         this.setValue(this.resetValue);
39363         this.originalValue = this.getValue();
39364         this.clearInvalid();
39365     },
39366
39367     // private
39368     initEvents : function(){
39369         // safari killled keypress - so keydown is now used..
39370         this.el.on("keydown" , this.fireKey,  this);
39371         this.el.on("focus", this.onFocus,  this);
39372         this.el.on("blur", this.onBlur,  this);
39373         this.el.relayEvent('keyup', this);
39374
39375         // reference to original value for reset
39376         this.originalValue = this.getValue();
39377         this.resetValue =  this.getValue();
39378     },
39379
39380     // private
39381     onFocus : function(){
39382         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39383             this.el.addClass(this.focusClass);
39384         }
39385         if(!this.hasFocus){
39386             this.hasFocus = true;
39387             this.startValue = this.getValue();
39388             this.fireEvent("focus", this);
39389         }
39390     },
39391
39392     beforeBlur : Roo.emptyFn,
39393
39394     // private
39395     onBlur : function(){
39396         this.beforeBlur();
39397         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39398             this.el.removeClass(this.focusClass);
39399         }
39400         this.hasFocus = false;
39401         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39402             this.validate();
39403         }
39404         var v = this.getValue();
39405         if(String(v) !== String(this.startValue)){
39406             this.fireEvent('change', this, v, this.startValue);
39407         }
39408         this.fireEvent("blur", this);
39409     },
39410
39411     /**
39412      * Returns whether or not the field value is currently valid
39413      * @param {Boolean} preventMark True to disable marking the field invalid
39414      * @return {Boolean} True if the value is valid, else false
39415      */
39416     isValid : function(preventMark){
39417         if(this.disabled){
39418             return true;
39419         }
39420         var restore = this.preventMark;
39421         this.preventMark = preventMark === true;
39422         var v = this.validateValue(this.processValue(this.getRawValue()));
39423         this.preventMark = restore;
39424         return v;
39425     },
39426
39427     /**
39428      * Validates the field value
39429      * @return {Boolean} True if the value is valid, else false
39430      */
39431     validate : function(){
39432         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39433             this.clearInvalid();
39434             return true;
39435         }
39436         return false;
39437     },
39438
39439     processValue : function(value){
39440         return value;
39441     },
39442
39443     // private
39444     // Subclasses should provide the validation implementation by overriding this
39445     validateValue : function(value){
39446         return true;
39447     },
39448
39449     /**
39450      * Mark this field as invalid
39451      * @param {String} msg The validation message
39452      */
39453     markInvalid : function(msg){
39454         if(!this.rendered || this.preventMark){ // not rendered
39455             return;
39456         }
39457         
39458         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39459         
39460         obj.el.addClass(this.invalidClass);
39461         msg = msg || this.invalidText;
39462         switch(this.msgTarget){
39463             case 'qtip':
39464                 obj.el.dom.qtip = msg;
39465                 obj.el.dom.qclass = 'x-form-invalid-tip';
39466                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39467                     Roo.QuickTips.enable();
39468                 }
39469                 break;
39470             case 'title':
39471                 this.el.dom.title = msg;
39472                 break;
39473             case 'under':
39474                 if(!this.errorEl){
39475                     var elp = this.el.findParent('.x-form-element', 5, true);
39476                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39477                     this.errorEl.setWidth(elp.getWidth(true)-20);
39478                 }
39479                 this.errorEl.update(msg);
39480                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39481                 break;
39482             case 'side':
39483                 if(!this.errorIcon){
39484                     var elp = this.el.findParent('.x-form-element', 5, true);
39485                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39486                 }
39487                 this.alignErrorIcon();
39488                 this.errorIcon.dom.qtip = msg;
39489                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39490                 this.errorIcon.show();
39491                 this.on('resize', this.alignErrorIcon, this);
39492                 break;
39493             default:
39494                 var t = Roo.getDom(this.msgTarget);
39495                 t.innerHTML = msg;
39496                 t.style.display = this.msgDisplay;
39497                 break;
39498         }
39499         this.fireEvent('invalid', this, msg);
39500     },
39501
39502     // private
39503     alignErrorIcon : function(){
39504         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39505     },
39506
39507     /**
39508      * Clear any invalid styles/messages for this field
39509      */
39510     clearInvalid : function(){
39511         if(!this.rendered || this.preventMark){ // not rendered
39512             return;
39513         }
39514         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39515         
39516         obj.el.removeClass(this.invalidClass);
39517         switch(this.msgTarget){
39518             case 'qtip':
39519                 obj.el.dom.qtip = '';
39520                 break;
39521             case 'title':
39522                 this.el.dom.title = '';
39523                 break;
39524             case 'under':
39525                 if(this.errorEl){
39526                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39527                 }
39528                 break;
39529             case 'side':
39530                 if(this.errorIcon){
39531                     this.errorIcon.dom.qtip = '';
39532                     this.errorIcon.hide();
39533                     this.un('resize', this.alignErrorIcon, this);
39534                 }
39535                 break;
39536             default:
39537                 var t = Roo.getDom(this.msgTarget);
39538                 t.innerHTML = '';
39539                 t.style.display = 'none';
39540                 break;
39541         }
39542         this.fireEvent('valid', this);
39543     },
39544
39545     /**
39546      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39547      * @return {Mixed} value The field value
39548      */
39549     getRawValue : function(){
39550         var v = this.el.getValue();
39551         
39552         return v;
39553     },
39554
39555     /**
39556      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39557      * @return {Mixed} value The field value
39558      */
39559     getValue : function(){
39560         var v = this.el.getValue();
39561          
39562         return v;
39563     },
39564
39565     /**
39566      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39567      * @param {Mixed} value The value to set
39568      */
39569     setRawValue : function(v){
39570         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39571     },
39572
39573     /**
39574      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39575      * @param {Mixed} value The value to set
39576      */
39577     setValue : function(v){
39578         this.value = v;
39579         if(this.rendered){
39580             this.el.dom.value = (v === null || v === undefined ? '' : v);
39581              this.validate();
39582         }
39583     },
39584
39585     adjustSize : function(w, h){
39586         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39587         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39588         return s;
39589     },
39590
39591     adjustWidth : function(tag, w){
39592         tag = tag.toLowerCase();
39593         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39594             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39595                 if(tag == 'input'){
39596                     return w + 2;
39597                 }
39598                 if(tag == 'textarea'){
39599                     return w-2;
39600                 }
39601             }else if(Roo.isOpera){
39602                 if(tag == 'input'){
39603                     return w + 2;
39604                 }
39605                 if(tag == 'textarea'){
39606                     return w-2;
39607                 }
39608             }
39609         }
39610         return w;
39611     }
39612 });
39613
39614
39615 // anything other than normal should be considered experimental
39616 Roo.form.Field.msgFx = {
39617     normal : {
39618         show: function(msgEl, f){
39619             msgEl.setDisplayed('block');
39620         },
39621
39622         hide : function(msgEl, f){
39623             msgEl.setDisplayed(false).update('');
39624         }
39625     },
39626
39627     slide : {
39628         show: function(msgEl, f){
39629             msgEl.slideIn('t', {stopFx:true});
39630         },
39631
39632         hide : function(msgEl, f){
39633             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39634         }
39635     },
39636
39637     slideRight : {
39638         show: function(msgEl, f){
39639             msgEl.fixDisplay();
39640             msgEl.alignTo(f.el, 'tl-tr');
39641             msgEl.slideIn('l', {stopFx:true});
39642         },
39643
39644         hide : function(msgEl, f){
39645             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39646         }
39647     }
39648 };/*
39649  * Based on:
39650  * Ext JS Library 1.1.1
39651  * Copyright(c) 2006-2007, Ext JS, LLC.
39652  *
39653  * Originally Released Under LGPL - original licence link has changed is not relivant.
39654  *
39655  * Fork - LGPL
39656  * <script type="text/javascript">
39657  */
39658  
39659
39660 /**
39661  * @class Roo.form.TextField
39662  * @extends Roo.form.Field
39663  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39664  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39665  * @constructor
39666  * Creates a new TextField
39667  * @param {Object} config Configuration options
39668  */
39669 Roo.form.TextField = function(config){
39670     Roo.form.TextField.superclass.constructor.call(this, config);
39671     this.addEvents({
39672         /**
39673          * @event autosize
39674          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39675          * according to the default logic, but this event provides a hook for the developer to apply additional
39676          * logic at runtime to resize the field if needed.
39677              * @param {Roo.form.Field} this This text field
39678              * @param {Number} width The new field width
39679              */
39680         autosize : true
39681     });
39682 };
39683
39684 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39685     /**
39686      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39687      */
39688     grow : false,
39689     /**
39690      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39691      */
39692     growMin : 30,
39693     /**
39694      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39695      */
39696     growMax : 800,
39697     /**
39698      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39699      */
39700     vtype : null,
39701     /**
39702      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39703      */
39704     maskRe : null,
39705     /**
39706      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39707      */
39708     disableKeyFilter : false,
39709     /**
39710      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39711      */
39712     allowBlank : true,
39713     /**
39714      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39715      */
39716     minLength : 0,
39717     /**
39718      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39719      */
39720     maxLength : Number.MAX_VALUE,
39721     /**
39722      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39723      */
39724     minLengthText : "The minimum length for this field is {0}",
39725     /**
39726      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39727      */
39728     maxLengthText : "The maximum length for this field is {0}",
39729     /**
39730      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39731      */
39732     selectOnFocus : false,
39733     /**
39734      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39735      */    
39736     allowLeadingSpace : false,
39737     /**
39738      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39739      */
39740     blankText : "This field is required",
39741     /**
39742      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39743      * If available, this function will be called only after the basic validators all return true, and will be passed the
39744      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39745      */
39746     validator : null,
39747     /**
39748      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39749      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39750      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39751      */
39752     regex : null,
39753     /**
39754      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39755      */
39756     regexText : "",
39757     /**
39758      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39759      */
39760     emptyText : null,
39761    
39762
39763     // private
39764     initEvents : function()
39765     {
39766         if (this.emptyText) {
39767             this.el.attr('placeholder', this.emptyText);
39768         }
39769         
39770         Roo.form.TextField.superclass.initEvents.call(this);
39771         if(this.validationEvent == 'keyup'){
39772             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39773             this.el.on('keyup', this.filterValidation, this);
39774         }
39775         else if(this.validationEvent !== false){
39776             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39777         }
39778         
39779         if(this.selectOnFocus){
39780             this.on("focus", this.preFocus, this);
39781         }
39782         if (!this.allowLeadingSpace) {
39783             this.on('blur', this.cleanLeadingSpace, this);
39784         }
39785         
39786         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39787             this.el.on("keypress", this.filterKeys, this);
39788         }
39789         if(this.grow){
39790             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39791             this.el.on("click", this.autoSize,  this);
39792         }
39793         if(this.el.is('input[type=password]') && Roo.isSafari){
39794             this.el.on('keydown', this.SafariOnKeyDown, this);
39795         }
39796     },
39797
39798     processValue : function(value){
39799         if(this.stripCharsRe){
39800             var newValue = value.replace(this.stripCharsRe, '');
39801             if(newValue !== value){
39802                 this.setRawValue(newValue);
39803                 return newValue;
39804             }
39805         }
39806         return value;
39807     },
39808
39809     filterValidation : function(e){
39810         if(!e.isNavKeyPress()){
39811             this.validationTask.delay(this.validationDelay);
39812         }
39813     },
39814
39815     // private
39816     onKeyUp : function(e){
39817         if(!e.isNavKeyPress()){
39818             this.autoSize();
39819         }
39820     },
39821     // private - clean the leading white space
39822     cleanLeadingSpace : function(e)
39823     {
39824         if ( this.inputType == 'file') {
39825             return;
39826         }
39827         
39828         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39829     },
39830     /**
39831      * Resets the current field value to the originally-loaded value and clears any validation messages.
39832      *  
39833      */
39834     reset : function(){
39835         Roo.form.TextField.superclass.reset.call(this);
39836        
39837     }, 
39838     // private
39839     preFocus : function(){
39840         
39841         if(this.selectOnFocus){
39842             this.el.dom.select();
39843         }
39844     },
39845
39846     
39847     // private
39848     filterKeys : function(e){
39849         var k = e.getKey();
39850         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39851             return;
39852         }
39853         var c = e.getCharCode(), cc = String.fromCharCode(c);
39854         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39855             return;
39856         }
39857         if(!this.maskRe.test(cc)){
39858             e.stopEvent();
39859         }
39860     },
39861
39862     setValue : function(v){
39863         
39864         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39865         
39866         this.autoSize();
39867     },
39868
39869     /**
39870      * Validates a value according to the field's validation rules and marks the field as invalid
39871      * if the validation fails
39872      * @param {Mixed} value The value to validate
39873      * @return {Boolean} True if the value is valid, else false
39874      */
39875     validateValue : function(value){
39876         if(value.length < 1)  { // if it's blank
39877              if(this.allowBlank){
39878                 this.clearInvalid();
39879                 return true;
39880              }else{
39881                 this.markInvalid(this.blankText);
39882                 return false;
39883              }
39884         }
39885         if(value.length < this.minLength){
39886             this.markInvalid(String.format(this.minLengthText, this.minLength));
39887             return false;
39888         }
39889         if(value.length > this.maxLength){
39890             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39891             return false;
39892         }
39893         if(this.vtype){
39894             var vt = Roo.form.VTypes;
39895             if(!vt[this.vtype](value, this)){
39896                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39897                 return false;
39898             }
39899         }
39900         if(typeof this.validator == "function"){
39901             var msg = this.validator(value);
39902             if(msg !== true){
39903                 this.markInvalid(msg);
39904                 return false;
39905             }
39906         }
39907         if(this.regex && !this.regex.test(value)){
39908             this.markInvalid(this.regexText);
39909             return false;
39910         }
39911         return true;
39912     },
39913
39914     /**
39915      * Selects text in this field
39916      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39917      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39918      */
39919     selectText : function(start, end){
39920         var v = this.getRawValue();
39921         if(v.length > 0){
39922             start = start === undefined ? 0 : start;
39923             end = end === undefined ? v.length : end;
39924             var d = this.el.dom;
39925             if(d.setSelectionRange){
39926                 d.setSelectionRange(start, end);
39927             }else if(d.createTextRange){
39928                 var range = d.createTextRange();
39929                 range.moveStart("character", start);
39930                 range.moveEnd("character", v.length-end);
39931                 range.select();
39932             }
39933         }
39934     },
39935
39936     /**
39937      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39938      * This only takes effect if grow = true, and fires the autosize event.
39939      */
39940     autoSize : function(){
39941         if(!this.grow || !this.rendered){
39942             return;
39943         }
39944         if(!this.metrics){
39945             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39946         }
39947         var el = this.el;
39948         var v = el.dom.value;
39949         var d = document.createElement('div');
39950         d.appendChild(document.createTextNode(v));
39951         v = d.innerHTML;
39952         d = null;
39953         v += "&#160;";
39954         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39955         this.el.setWidth(w);
39956         this.fireEvent("autosize", this, w);
39957     },
39958     
39959     // private
39960     SafariOnKeyDown : function(event)
39961     {
39962         // this is a workaround for a password hang bug on chrome/ webkit.
39963         
39964         var isSelectAll = false;
39965         
39966         if(this.el.dom.selectionEnd > 0){
39967             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39968         }
39969         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39970             event.preventDefault();
39971             this.setValue('');
39972             return;
39973         }
39974         
39975         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39976             
39977             event.preventDefault();
39978             // this is very hacky as keydown always get's upper case.
39979             
39980             var cc = String.fromCharCode(event.getCharCode());
39981             
39982             
39983             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39984             
39985         }
39986         
39987         
39988     }
39989 });/*
39990  * Based on:
39991  * Ext JS Library 1.1.1
39992  * Copyright(c) 2006-2007, Ext JS, LLC.
39993  *
39994  * Originally Released Under LGPL - original licence link has changed is not relivant.
39995  *
39996  * Fork - LGPL
39997  * <script type="text/javascript">
39998  */
39999  
40000 /**
40001  * @class Roo.form.Hidden
40002  * @extends Roo.form.TextField
40003  * Simple Hidden element used on forms 
40004  * 
40005  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40006  * 
40007  * @constructor
40008  * Creates a new Hidden form element.
40009  * @param {Object} config Configuration options
40010  */
40011
40012
40013
40014 // easy hidden field...
40015 Roo.form.Hidden = function(config){
40016     Roo.form.Hidden.superclass.constructor.call(this, config);
40017 };
40018   
40019 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40020     fieldLabel:      '',
40021     inputType:      'hidden',
40022     width:          50,
40023     allowBlank:     true,
40024     labelSeparator: '',
40025     hidden:         true,
40026     itemCls :       'x-form-item-display-none'
40027
40028
40029 });
40030
40031
40032 /*
40033  * Based on:
40034  * Ext JS Library 1.1.1
40035  * Copyright(c) 2006-2007, Ext JS, LLC.
40036  *
40037  * Originally Released Under LGPL - original licence link has changed is not relivant.
40038  *
40039  * Fork - LGPL
40040  * <script type="text/javascript">
40041  */
40042  
40043 /**
40044  * @class Roo.form.TriggerField
40045  * @extends Roo.form.TextField
40046  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40047  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40048  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40049  * for which you can provide a custom implementation.  For example:
40050  * <pre><code>
40051 var trigger = new Roo.form.TriggerField();
40052 trigger.onTriggerClick = myTriggerFn;
40053 trigger.applyTo('my-field');
40054 </code></pre>
40055  *
40056  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40057  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40058  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40059  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40060  * @constructor
40061  * Create a new TriggerField.
40062  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40063  * to the base TextField)
40064  */
40065 Roo.form.TriggerField = function(config){
40066     this.mimicing = false;
40067     Roo.form.TriggerField.superclass.constructor.call(this, config);
40068 };
40069
40070 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40071     /**
40072      * @cfg {String} triggerClass A CSS class to apply to the trigger
40073      */
40074     /**
40075      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40076      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40077      */
40078     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40079     /**
40080      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40081      */
40082     hideTrigger:false,
40083
40084     /** @cfg {Boolean} grow @hide */
40085     /** @cfg {Number} growMin @hide */
40086     /** @cfg {Number} growMax @hide */
40087
40088     /**
40089      * @hide 
40090      * @method
40091      */
40092     autoSize: Roo.emptyFn,
40093     // private
40094     monitorTab : true,
40095     // private
40096     deferHeight : true,
40097
40098     
40099     actionMode : 'wrap',
40100     // private
40101     onResize : function(w, h){
40102         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40103         if(typeof w == 'number'){
40104             var x = w - this.trigger.getWidth();
40105             this.el.setWidth(this.adjustWidth('input', x));
40106             this.trigger.setStyle('left', x+'px');
40107         }
40108     },
40109
40110     // private
40111     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40112
40113     // private
40114     getResizeEl : function(){
40115         return this.wrap;
40116     },
40117
40118     // private
40119     getPositionEl : function(){
40120         return this.wrap;
40121     },
40122
40123     // private
40124     alignErrorIcon : function(){
40125         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40126     },
40127
40128     // private
40129     onRender : function(ct, position){
40130         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40131         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40132         this.trigger = this.wrap.createChild(this.triggerConfig ||
40133                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40134         if(this.hideTrigger){
40135             this.trigger.setDisplayed(false);
40136         }
40137         this.initTrigger();
40138         if(!this.width){
40139             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40140         }
40141     },
40142
40143     // private
40144     initTrigger : function(){
40145         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40146         this.trigger.addClassOnOver('x-form-trigger-over');
40147         this.trigger.addClassOnClick('x-form-trigger-click');
40148     },
40149
40150     // private
40151     onDestroy : function(){
40152         if(this.trigger){
40153             this.trigger.removeAllListeners();
40154             this.trigger.remove();
40155         }
40156         if(this.wrap){
40157             this.wrap.remove();
40158         }
40159         Roo.form.TriggerField.superclass.onDestroy.call(this);
40160     },
40161
40162     // private
40163     onFocus : function(){
40164         Roo.form.TriggerField.superclass.onFocus.call(this);
40165         if(!this.mimicing){
40166             this.wrap.addClass('x-trigger-wrap-focus');
40167             this.mimicing = true;
40168             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40169             if(this.monitorTab){
40170                 this.el.on("keydown", this.checkTab, this);
40171             }
40172         }
40173     },
40174
40175     // private
40176     checkTab : function(e){
40177         if(e.getKey() == e.TAB){
40178             this.triggerBlur();
40179         }
40180     },
40181
40182     // private
40183     onBlur : function(){
40184         // do nothing
40185     },
40186
40187     // private
40188     mimicBlur : function(e, t){
40189         if(!this.wrap.contains(t) && this.validateBlur()){
40190             this.triggerBlur();
40191         }
40192     },
40193
40194     // private
40195     triggerBlur : function(){
40196         this.mimicing = false;
40197         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40198         if(this.monitorTab){
40199             this.el.un("keydown", this.checkTab, this);
40200         }
40201         this.wrap.removeClass('x-trigger-wrap-focus');
40202         Roo.form.TriggerField.superclass.onBlur.call(this);
40203     },
40204
40205     // private
40206     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40207     validateBlur : function(e, t){
40208         return true;
40209     },
40210
40211     // private
40212     onDisable : function(){
40213         Roo.form.TriggerField.superclass.onDisable.call(this);
40214         if(this.wrap){
40215             this.wrap.addClass('x-item-disabled');
40216         }
40217     },
40218
40219     // private
40220     onEnable : function(){
40221         Roo.form.TriggerField.superclass.onEnable.call(this);
40222         if(this.wrap){
40223             this.wrap.removeClass('x-item-disabled');
40224         }
40225     },
40226
40227     // private
40228     onShow : function(){
40229         var ae = this.getActionEl();
40230         
40231         if(ae){
40232             ae.dom.style.display = '';
40233             ae.dom.style.visibility = 'visible';
40234         }
40235     },
40236
40237     // private
40238     
40239     onHide : function(){
40240         var ae = this.getActionEl();
40241         ae.dom.style.display = 'none';
40242     },
40243
40244     /**
40245      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40246      * by an implementing function.
40247      * @method
40248      * @param {EventObject} e
40249      */
40250     onTriggerClick : Roo.emptyFn
40251 });
40252
40253 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40254 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40255 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40256 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40257     initComponent : function(){
40258         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40259
40260         this.triggerConfig = {
40261             tag:'span', cls:'x-form-twin-triggers', cn:[
40262             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40263             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40264         ]};
40265     },
40266
40267     getTrigger : function(index){
40268         return this.triggers[index];
40269     },
40270
40271     initTrigger : function(){
40272         var ts = this.trigger.select('.x-form-trigger', true);
40273         this.wrap.setStyle('overflow', 'hidden');
40274         var triggerField = this;
40275         ts.each(function(t, all, index){
40276             t.hide = function(){
40277                 var w = triggerField.wrap.getWidth();
40278                 this.dom.style.display = 'none';
40279                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40280             };
40281             t.show = function(){
40282                 var w = triggerField.wrap.getWidth();
40283                 this.dom.style.display = '';
40284                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40285             };
40286             var triggerIndex = 'Trigger'+(index+1);
40287
40288             if(this['hide'+triggerIndex]){
40289                 t.dom.style.display = 'none';
40290             }
40291             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40292             t.addClassOnOver('x-form-trigger-over');
40293             t.addClassOnClick('x-form-trigger-click');
40294         }, this);
40295         this.triggers = ts.elements;
40296     },
40297
40298     onTrigger1Click : Roo.emptyFn,
40299     onTrigger2Click : Roo.emptyFn
40300 });/*
40301  * Based on:
40302  * Ext JS Library 1.1.1
40303  * Copyright(c) 2006-2007, Ext JS, LLC.
40304  *
40305  * Originally Released Under LGPL - original licence link has changed is not relivant.
40306  *
40307  * Fork - LGPL
40308  * <script type="text/javascript">
40309  */
40310  
40311 /**
40312  * @class Roo.form.TextArea
40313  * @extends Roo.form.TextField
40314  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40315  * support for auto-sizing.
40316  * @constructor
40317  * Creates a new TextArea
40318  * @param {Object} config Configuration options
40319  */
40320 Roo.form.TextArea = function(config){
40321     Roo.form.TextArea.superclass.constructor.call(this, config);
40322     // these are provided exchanges for backwards compat
40323     // minHeight/maxHeight were replaced by growMin/growMax to be
40324     // compatible with TextField growing config values
40325     if(this.minHeight !== undefined){
40326         this.growMin = this.minHeight;
40327     }
40328     if(this.maxHeight !== undefined){
40329         this.growMax = this.maxHeight;
40330     }
40331 };
40332
40333 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40334     /**
40335      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40336      */
40337     growMin : 60,
40338     /**
40339      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40340      */
40341     growMax: 1000,
40342     /**
40343      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40344      * in the field (equivalent to setting overflow: hidden, defaults to false)
40345      */
40346     preventScrollbars: false,
40347     /**
40348      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40349      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40350      */
40351
40352     // private
40353     onRender : function(ct, position){
40354         if(!this.el){
40355             this.defaultAutoCreate = {
40356                 tag: "textarea",
40357                 style:"width:300px;height:60px;",
40358                 autocomplete: "new-password"
40359             };
40360         }
40361         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40362         if(this.grow){
40363             this.textSizeEl = Roo.DomHelper.append(document.body, {
40364                 tag: "pre", cls: "x-form-grow-sizer"
40365             });
40366             if(this.preventScrollbars){
40367                 this.el.setStyle("overflow", "hidden");
40368             }
40369             this.el.setHeight(this.growMin);
40370         }
40371     },
40372
40373     onDestroy : function(){
40374         if(this.textSizeEl){
40375             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40376         }
40377         Roo.form.TextArea.superclass.onDestroy.call(this);
40378     },
40379
40380     // private
40381     onKeyUp : function(e){
40382         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40383             this.autoSize();
40384         }
40385     },
40386
40387     /**
40388      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40389      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40390      */
40391     autoSize : function(){
40392         if(!this.grow || !this.textSizeEl){
40393             return;
40394         }
40395         var el = this.el;
40396         var v = el.dom.value;
40397         var ts = this.textSizeEl;
40398
40399         ts.innerHTML = '';
40400         ts.appendChild(document.createTextNode(v));
40401         v = ts.innerHTML;
40402
40403         Roo.fly(ts).setWidth(this.el.getWidth());
40404         if(v.length < 1){
40405             v = "&#160;&#160;";
40406         }else{
40407             if(Roo.isIE){
40408                 v = v.replace(/\n/g, '<p>&#160;</p>');
40409             }
40410             v += "&#160;\n&#160;";
40411         }
40412         ts.innerHTML = v;
40413         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40414         if(h != this.lastHeight){
40415             this.lastHeight = h;
40416             this.el.setHeight(h);
40417             this.fireEvent("autosize", this, h);
40418         }
40419     }
40420 });/*
40421  * Based on:
40422  * Ext JS Library 1.1.1
40423  * Copyright(c) 2006-2007, Ext JS, LLC.
40424  *
40425  * Originally Released Under LGPL - original licence link has changed is not relivant.
40426  *
40427  * Fork - LGPL
40428  * <script type="text/javascript">
40429  */
40430  
40431
40432 /**
40433  * @class Roo.form.NumberField
40434  * @extends Roo.form.TextField
40435  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40436  * @constructor
40437  * Creates a new NumberField
40438  * @param {Object} config Configuration options
40439  */
40440 Roo.form.NumberField = function(config){
40441     Roo.form.NumberField.superclass.constructor.call(this, config);
40442 };
40443
40444 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40445     /**
40446      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40447      */
40448     fieldClass: "x-form-field x-form-num-field",
40449     /**
40450      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40451      */
40452     allowDecimals : true,
40453     /**
40454      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40455      */
40456     decimalSeparator : ".",
40457     /**
40458      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40459      */
40460     decimalPrecision : 2,
40461     /**
40462      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40463      */
40464     allowNegative : true,
40465     /**
40466      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40467      */
40468     minValue : Number.NEGATIVE_INFINITY,
40469     /**
40470      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40471      */
40472     maxValue : Number.MAX_VALUE,
40473     /**
40474      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40475      */
40476     minText : "The minimum value for this field is {0}",
40477     /**
40478      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40479      */
40480     maxText : "The maximum value for this field is {0}",
40481     /**
40482      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40483      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40484      */
40485     nanText : "{0} is not a valid number",
40486
40487     // private
40488     initEvents : function(){
40489         Roo.form.NumberField.superclass.initEvents.call(this);
40490         var allowed = "0123456789";
40491         if(this.allowDecimals){
40492             allowed += this.decimalSeparator;
40493         }
40494         if(this.allowNegative){
40495             allowed += "-";
40496         }
40497         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40498         var keyPress = function(e){
40499             var k = e.getKey();
40500             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40501                 return;
40502             }
40503             var c = e.getCharCode();
40504             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40505                 e.stopEvent();
40506             }
40507         };
40508         this.el.on("keypress", keyPress, this);
40509     },
40510
40511     // private
40512     validateValue : function(value){
40513         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40514             return false;
40515         }
40516         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40517              return true;
40518         }
40519         var num = this.parseValue(value);
40520         if(isNaN(num)){
40521             this.markInvalid(String.format(this.nanText, value));
40522             return false;
40523         }
40524         if(num < this.minValue){
40525             this.markInvalid(String.format(this.minText, this.minValue));
40526             return false;
40527         }
40528         if(num > this.maxValue){
40529             this.markInvalid(String.format(this.maxText, this.maxValue));
40530             return false;
40531         }
40532         return true;
40533     },
40534
40535     getValue : function(){
40536         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40537     },
40538
40539     // private
40540     parseValue : function(value){
40541         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40542         return isNaN(value) ? '' : value;
40543     },
40544
40545     // private
40546     fixPrecision : function(value){
40547         var nan = isNaN(value);
40548         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40549             return nan ? '' : value;
40550         }
40551         return parseFloat(value).toFixed(this.decimalPrecision);
40552     },
40553
40554     setValue : function(v){
40555         v = this.fixPrecision(v);
40556         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40557     },
40558
40559     // private
40560     decimalPrecisionFcn : function(v){
40561         return Math.floor(v);
40562     },
40563
40564     beforeBlur : function(){
40565         var v = this.parseValue(this.getRawValue());
40566         if(v){
40567             this.setValue(v);
40568         }
40569     }
40570 });/*
40571  * Based on:
40572  * Ext JS Library 1.1.1
40573  * Copyright(c) 2006-2007, Ext JS, LLC.
40574  *
40575  * Originally Released Under LGPL - original licence link has changed is not relivant.
40576  *
40577  * Fork - LGPL
40578  * <script type="text/javascript">
40579  */
40580  
40581 /**
40582  * @class Roo.form.DateField
40583  * @extends Roo.form.TriggerField
40584  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40585 * @constructor
40586 * Create a new DateField
40587 * @param {Object} config
40588  */
40589 Roo.form.DateField = function(config)
40590 {
40591     Roo.form.DateField.superclass.constructor.call(this, config);
40592     
40593       this.addEvents({
40594          
40595         /**
40596          * @event select
40597          * Fires when a date is selected
40598              * @param {Roo.form.DateField} combo This combo box
40599              * @param {Date} date The date selected
40600              */
40601         'select' : true
40602          
40603     });
40604     
40605     
40606     if(typeof this.minValue == "string") {
40607         this.minValue = this.parseDate(this.minValue);
40608     }
40609     if(typeof this.maxValue == "string") {
40610         this.maxValue = this.parseDate(this.maxValue);
40611     }
40612     this.ddMatch = null;
40613     if(this.disabledDates){
40614         var dd = this.disabledDates;
40615         var re = "(?:";
40616         for(var i = 0; i < dd.length; i++){
40617             re += dd[i];
40618             if(i != dd.length-1) {
40619                 re += "|";
40620             }
40621         }
40622         this.ddMatch = new RegExp(re + ")");
40623     }
40624 };
40625
40626 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40627     /**
40628      * @cfg {String} format
40629      * The default date format string which can be overriden for localization support.  The format must be
40630      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40631      */
40632     format : "m/d/y",
40633     /**
40634      * @cfg {String} altFormats
40635      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40636      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40637      */
40638     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40639     /**
40640      * @cfg {Array} disabledDays
40641      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40642      */
40643     disabledDays : null,
40644     /**
40645      * @cfg {String} disabledDaysText
40646      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40647      */
40648     disabledDaysText : "Disabled",
40649     /**
40650      * @cfg {Array} disabledDates
40651      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40652      * expression so they are very powerful. Some examples:
40653      * <ul>
40654      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40655      * <li>["03/08", "09/16"] would disable those days for every year</li>
40656      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40657      * <li>["03/../2006"] would disable every day in March 2006</li>
40658      * <li>["^03"] would disable every day in every March</li>
40659      * </ul>
40660      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40661      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40662      */
40663     disabledDates : null,
40664     /**
40665      * @cfg {String} disabledDatesText
40666      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40667      */
40668     disabledDatesText : "Disabled",
40669     /**
40670      * @cfg {Date/String} minValue
40671      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40672      * valid format (defaults to null).
40673      */
40674     minValue : null,
40675     /**
40676      * @cfg {Date/String} maxValue
40677      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40678      * valid format (defaults to null).
40679      */
40680     maxValue : null,
40681     /**
40682      * @cfg {String} minText
40683      * The error text to display when the date in the cell is before minValue (defaults to
40684      * 'The date in this field must be after {minValue}').
40685      */
40686     minText : "The date in this field must be equal to or after {0}",
40687     /**
40688      * @cfg {String} maxText
40689      * The error text to display when the date in the cell is after maxValue (defaults to
40690      * 'The date in this field must be before {maxValue}').
40691      */
40692     maxText : "The date in this field must be equal to or before {0}",
40693     /**
40694      * @cfg {String} invalidText
40695      * The error text to display when the date in the field is invalid (defaults to
40696      * '{value} is not a valid date - it must be in the format {format}').
40697      */
40698     invalidText : "{0} is not a valid date - it must be in the format {1}",
40699     /**
40700      * @cfg {String} triggerClass
40701      * An additional CSS class used to style the trigger button.  The trigger will always get the
40702      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40703      * which displays a calendar icon).
40704      */
40705     triggerClass : 'x-form-date-trigger',
40706     
40707
40708     /**
40709      * @cfg {Boolean} useIso
40710      * if enabled, then the date field will use a hidden field to store the 
40711      * real value as iso formated date. default (false)
40712      */ 
40713     useIso : false,
40714     /**
40715      * @cfg {String/Object} autoCreate
40716      * A DomHelper element spec, or true for a default element spec (defaults to
40717      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40718      */ 
40719     // private
40720     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40721     
40722     // private
40723     hiddenField: false,
40724     
40725     onRender : function(ct, position)
40726     {
40727         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40728         if (this.useIso) {
40729             //this.el.dom.removeAttribute('name'); 
40730             Roo.log("Changing name?");
40731             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40732             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40733                     'before', true);
40734             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40735             // prevent input submission
40736             this.hiddenName = this.name;
40737         }
40738             
40739             
40740     },
40741     
40742     // private
40743     validateValue : function(value)
40744     {
40745         value = this.formatDate(value);
40746         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40747             Roo.log('super failed');
40748             return false;
40749         }
40750         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40751              return true;
40752         }
40753         var svalue = value;
40754         value = this.parseDate(value);
40755         if(!value){
40756             Roo.log('parse date failed' + svalue);
40757             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40758             return false;
40759         }
40760         var time = value.getTime();
40761         if(this.minValue && time < this.minValue.getTime()){
40762             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40763             return false;
40764         }
40765         if(this.maxValue && time > this.maxValue.getTime()){
40766             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40767             return false;
40768         }
40769         if(this.disabledDays){
40770             var day = value.getDay();
40771             for(var i = 0; i < this.disabledDays.length; i++) {
40772                 if(day === this.disabledDays[i]){
40773                     this.markInvalid(this.disabledDaysText);
40774                     return false;
40775                 }
40776             }
40777         }
40778         var fvalue = this.formatDate(value);
40779         if(this.ddMatch && this.ddMatch.test(fvalue)){
40780             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40781             return false;
40782         }
40783         return true;
40784     },
40785
40786     // private
40787     // Provides logic to override the default TriggerField.validateBlur which just returns true
40788     validateBlur : function(){
40789         return !this.menu || !this.menu.isVisible();
40790     },
40791     
40792     getName: function()
40793     {
40794         // returns hidden if it's set..
40795         if (!this.rendered) {return ''};
40796         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40797         
40798     },
40799
40800     /**
40801      * Returns the current date value of the date field.
40802      * @return {Date} The date value
40803      */
40804     getValue : function(){
40805         
40806         return  this.hiddenField ?
40807                 this.hiddenField.value :
40808                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40809     },
40810
40811     /**
40812      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40813      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40814      * (the default format used is "m/d/y").
40815      * <br />Usage:
40816      * <pre><code>
40817 //All of these calls set the same date value (May 4, 2006)
40818
40819 //Pass a date object:
40820 var dt = new Date('5/4/06');
40821 dateField.setValue(dt);
40822
40823 //Pass a date string (default format):
40824 dateField.setValue('5/4/06');
40825
40826 //Pass a date string (custom format):
40827 dateField.format = 'Y-m-d';
40828 dateField.setValue('2006-5-4');
40829 </code></pre>
40830      * @param {String/Date} date The date or valid date string
40831      */
40832     setValue : function(date){
40833         if (this.hiddenField) {
40834             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40835         }
40836         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40837         // make sure the value field is always stored as a date..
40838         this.value = this.parseDate(date);
40839         
40840         
40841     },
40842
40843     // private
40844     parseDate : function(value){
40845         if(!value || value instanceof Date){
40846             return value;
40847         }
40848         var v = Date.parseDate(value, this.format);
40849          if (!v && this.useIso) {
40850             v = Date.parseDate(value, 'Y-m-d');
40851         }
40852         if(!v && this.altFormats){
40853             if(!this.altFormatsArray){
40854                 this.altFormatsArray = this.altFormats.split("|");
40855             }
40856             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40857                 v = Date.parseDate(value, this.altFormatsArray[i]);
40858             }
40859         }
40860         return v;
40861     },
40862
40863     // private
40864     formatDate : function(date, fmt){
40865         return (!date || !(date instanceof Date)) ?
40866                date : date.dateFormat(fmt || this.format);
40867     },
40868
40869     // private
40870     menuListeners : {
40871         select: function(m, d){
40872             
40873             this.setValue(d);
40874             this.fireEvent('select', this, d);
40875         },
40876         show : function(){ // retain focus styling
40877             this.onFocus();
40878         },
40879         hide : function(){
40880             this.focus.defer(10, this);
40881             var ml = this.menuListeners;
40882             this.menu.un("select", ml.select,  this);
40883             this.menu.un("show", ml.show,  this);
40884             this.menu.un("hide", ml.hide,  this);
40885         }
40886     },
40887
40888     // private
40889     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40890     onTriggerClick : function(){
40891         if(this.disabled){
40892             return;
40893         }
40894         if(this.menu == null){
40895             this.menu = new Roo.menu.DateMenu();
40896         }
40897         Roo.apply(this.menu.picker,  {
40898             showClear: this.allowBlank,
40899             minDate : this.minValue,
40900             maxDate : this.maxValue,
40901             disabledDatesRE : this.ddMatch,
40902             disabledDatesText : this.disabledDatesText,
40903             disabledDays : this.disabledDays,
40904             disabledDaysText : this.disabledDaysText,
40905             format : this.useIso ? 'Y-m-d' : this.format,
40906             minText : String.format(this.minText, this.formatDate(this.minValue)),
40907             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40908         });
40909         this.menu.on(Roo.apply({}, this.menuListeners, {
40910             scope:this
40911         }));
40912         this.menu.picker.setValue(this.getValue() || new Date());
40913         this.menu.show(this.el, "tl-bl?");
40914     },
40915
40916     beforeBlur : function(){
40917         var v = this.parseDate(this.getRawValue());
40918         if(v){
40919             this.setValue(v);
40920         }
40921     },
40922
40923     /*@
40924      * overide
40925      * 
40926      */
40927     isDirty : function() {
40928         if(this.disabled) {
40929             return false;
40930         }
40931         
40932         if(typeof(this.startValue) === 'undefined'){
40933             return false;
40934         }
40935         
40936         return String(this.getValue()) !== String(this.startValue);
40937         
40938     },
40939     // @overide
40940     cleanLeadingSpace : function(e)
40941     {
40942        return;
40943     }
40944     
40945 });/*
40946  * Based on:
40947  * Ext JS Library 1.1.1
40948  * Copyright(c) 2006-2007, Ext JS, LLC.
40949  *
40950  * Originally Released Under LGPL - original licence link has changed is not relivant.
40951  *
40952  * Fork - LGPL
40953  * <script type="text/javascript">
40954  */
40955  
40956 /**
40957  * @class Roo.form.MonthField
40958  * @extends Roo.form.TriggerField
40959  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40960 * @constructor
40961 * Create a new MonthField
40962 * @param {Object} config
40963  */
40964 Roo.form.MonthField = function(config){
40965     
40966     Roo.form.MonthField.superclass.constructor.call(this, config);
40967     
40968       this.addEvents({
40969          
40970         /**
40971          * @event select
40972          * Fires when a date is selected
40973              * @param {Roo.form.MonthFieeld} combo This combo box
40974              * @param {Date} date The date selected
40975              */
40976         'select' : true
40977          
40978     });
40979     
40980     
40981     if(typeof this.minValue == "string") {
40982         this.minValue = this.parseDate(this.minValue);
40983     }
40984     if(typeof this.maxValue == "string") {
40985         this.maxValue = this.parseDate(this.maxValue);
40986     }
40987     this.ddMatch = null;
40988     if(this.disabledDates){
40989         var dd = this.disabledDates;
40990         var re = "(?:";
40991         for(var i = 0; i < dd.length; i++){
40992             re += dd[i];
40993             if(i != dd.length-1) {
40994                 re += "|";
40995             }
40996         }
40997         this.ddMatch = new RegExp(re + ")");
40998     }
40999 };
41000
41001 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41002     /**
41003      * @cfg {String} format
41004      * The default date format string which can be overriden for localization support.  The format must be
41005      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41006      */
41007     format : "M Y",
41008     /**
41009      * @cfg {String} altFormats
41010      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41011      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41012      */
41013     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41014     /**
41015      * @cfg {Array} disabledDays
41016      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41017      */
41018     disabledDays : [0,1,2,3,4,5,6],
41019     /**
41020      * @cfg {String} disabledDaysText
41021      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41022      */
41023     disabledDaysText : "Disabled",
41024     /**
41025      * @cfg {Array} disabledDates
41026      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41027      * expression so they are very powerful. Some examples:
41028      * <ul>
41029      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41030      * <li>["03/08", "09/16"] would disable those days for every year</li>
41031      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41032      * <li>["03/../2006"] would disable every day in March 2006</li>
41033      * <li>["^03"] would disable every day in every March</li>
41034      * </ul>
41035      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41036      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41037      */
41038     disabledDates : null,
41039     /**
41040      * @cfg {String} disabledDatesText
41041      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41042      */
41043     disabledDatesText : "Disabled",
41044     /**
41045      * @cfg {Date/String} minValue
41046      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41047      * valid format (defaults to null).
41048      */
41049     minValue : null,
41050     /**
41051      * @cfg {Date/String} maxValue
41052      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41053      * valid format (defaults to null).
41054      */
41055     maxValue : null,
41056     /**
41057      * @cfg {String} minText
41058      * The error text to display when the date in the cell is before minValue (defaults to
41059      * 'The date in this field must be after {minValue}').
41060      */
41061     minText : "The date in this field must be equal to or after {0}",
41062     /**
41063      * @cfg {String} maxTextf
41064      * The error text to display when the date in the cell is after maxValue (defaults to
41065      * 'The date in this field must be before {maxValue}').
41066      */
41067     maxText : "The date in this field must be equal to or before {0}",
41068     /**
41069      * @cfg {String} invalidText
41070      * The error text to display when the date in the field is invalid (defaults to
41071      * '{value} is not a valid date - it must be in the format {format}').
41072      */
41073     invalidText : "{0} is not a valid date - it must be in the format {1}",
41074     /**
41075      * @cfg {String} triggerClass
41076      * An additional CSS class used to style the trigger button.  The trigger will always get the
41077      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41078      * which displays a calendar icon).
41079      */
41080     triggerClass : 'x-form-date-trigger',
41081     
41082
41083     /**
41084      * @cfg {Boolean} useIso
41085      * if enabled, then the date field will use a hidden field to store the 
41086      * real value as iso formated date. default (true)
41087      */ 
41088     useIso : true,
41089     /**
41090      * @cfg {String/Object} autoCreate
41091      * A DomHelper element spec, or true for a default element spec (defaults to
41092      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41093      */ 
41094     // private
41095     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41096     
41097     // private
41098     hiddenField: false,
41099     
41100     hideMonthPicker : false,
41101     
41102     onRender : function(ct, position)
41103     {
41104         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41105         if (this.useIso) {
41106             this.el.dom.removeAttribute('name'); 
41107             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41108                     'before', true);
41109             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41110             // prevent input submission
41111             this.hiddenName = this.name;
41112         }
41113             
41114             
41115     },
41116     
41117     // private
41118     validateValue : function(value)
41119     {
41120         value = this.formatDate(value);
41121         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41122             return false;
41123         }
41124         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41125              return true;
41126         }
41127         var svalue = value;
41128         value = this.parseDate(value);
41129         if(!value){
41130             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41131             return false;
41132         }
41133         var time = value.getTime();
41134         if(this.minValue && time < this.minValue.getTime()){
41135             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41136             return false;
41137         }
41138         if(this.maxValue && time > this.maxValue.getTime()){
41139             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41140             return false;
41141         }
41142         /*if(this.disabledDays){
41143             var day = value.getDay();
41144             for(var i = 0; i < this.disabledDays.length; i++) {
41145                 if(day === this.disabledDays[i]){
41146                     this.markInvalid(this.disabledDaysText);
41147                     return false;
41148                 }
41149             }
41150         }
41151         */
41152         var fvalue = this.formatDate(value);
41153         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41154             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41155             return false;
41156         }
41157         */
41158         return true;
41159     },
41160
41161     // private
41162     // Provides logic to override the default TriggerField.validateBlur which just returns true
41163     validateBlur : function(){
41164         return !this.menu || !this.menu.isVisible();
41165     },
41166
41167     /**
41168      * Returns the current date value of the date field.
41169      * @return {Date} The date value
41170      */
41171     getValue : function(){
41172         
41173         
41174         
41175         return  this.hiddenField ?
41176                 this.hiddenField.value :
41177                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41178     },
41179
41180     /**
41181      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41182      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41183      * (the default format used is "m/d/y").
41184      * <br />Usage:
41185      * <pre><code>
41186 //All of these calls set the same date value (May 4, 2006)
41187
41188 //Pass a date object:
41189 var dt = new Date('5/4/06');
41190 monthField.setValue(dt);
41191
41192 //Pass a date string (default format):
41193 monthField.setValue('5/4/06');
41194
41195 //Pass a date string (custom format):
41196 monthField.format = 'Y-m-d';
41197 monthField.setValue('2006-5-4');
41198 </code></pre>
41199      * @param {String/Date} date The date or valid date string
41200      */
41201     setValue : function(date){
41202         Roo.log('month setValue' + date);
41203         // can only be first of month..
41204         
41205         var val = this.parseDate(date);
41206         
41207         if (this.hiddenField) {
41208             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41209         }
41210         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41211         this.value = this.parseDate(date);
41212     },
41213
41214     // private
41215     parseDate : function(value){
41216         if(!value || value instanceof Date){
41217             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41218             return value;
41219         }
41220         var v = Date.parseDate(value, this.format);
41221         if (!v && this.useIso) {
41222             v = Date.parseDate(value, 'Y-m-d');
41223         }
41224         if (v) {
41225             // 
41226             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41227         }
41228         
41229         
41230         if(!v && this.altFormats){
41231             if(!this.altFormatsArray){
41232                 this.altFormatsArray = this.altFormats.split("|");
41233             }
41234             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41235                 v = Date.parseDate(value, this.altFormatsArray[i]);
41236             }
41237         }
41238         return v;
41239     },
41240
41241     // private
41242     formatDate : function(date, fmt){
41243         return (!date || !(date instanceof Date)) ?
41244                date : date.dateFormat(fmt || this.format);
41245     },
41246
41247     // private
41248     menuListeners : {
41249         select: function(m, d){
41250             this.setValue(d);
41251             this.fireEvent('select', this, d);
41252         },
41253         show : function(){ // retain focus styling
41254             this.onFocus();
41255         },
41256         hide : function(){
41257             this.focus.defer(10, this);
41258             var ml = this.menuListeners;
41259             this.menu.un("select", ml.select,  this);
41260             this.menu.un("show", ml.show,  this);
41261             this.menu.un("hide", ml.hide,  this);
41262         }
41263     },
41264     // private
41265     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41266     onTriggerClick : function(){
41267         if(this.disabled){
41268             return;
41269         }
41270         if(this.menu == null){
41271             this.menu = new Roo.menu.DateMenu();
41272            
41273         }
41274         
41275         Roo.apply(this.menu.picker,  {
41276             
41277             showClear: this.allowBlank,
41278             minDate : this.minValue,
41279             maxDate : this.maxValue,
41280             disabledDatesRE : this.ddMatch,
41281             disabledDatesText : this.disabledDatesText,
41282             
41283             format : this.useIso ? 'Y-m-d' : this.format,
41284             minText : String.format(this.minText, this.formatDate(this.minValue)),
41285             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41286             
41287         });
41288          this.menu.on(Roo.apply({}, this.menuListeners, {
41289             scope:this
41290         }));
41291        
41292         
41293         var m = this.menu;
41294         var p = m.picker;
41295         
41296         // hide month picker get's called when we called by 'before hide';
41297         
41298         var ignorehide = true;
41299         p.hideMonthPicker  = function(disableAnim){
41300             if (ignorehide) {
41301                 return;
41302             }
41303              if(this.monthPicker){
41304                 Roo.log("hideMonthPicker called");
41305                 if(disableAnim === true){
41306                     this.monthPicker.hide();
41307                 }else{
41308                     this.monthPicker.slideOut('t', {duration:.2});
41309                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41310                     p.fireEvent("select", this, this.value);
41311                     m.hide();
41312                 }
41313             }
41314         }
41315         
41316         Roo.log('picker set value');
41317         Roo.log(this.getValue());
41318         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41319         m.show(this.el, 'tl-bl?');
41320         ignorehide  = false;
41321         // this will trigger hideMonthPicker..
41322         
41323         
41324         // hidden the day picker
41325         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41326         
41327         
41328         
41329       
41330         
41331         p.showMonthPicker.defer(100, p);
41332     
41333         
41334        
41335     },
41336
41337     beforeBlur : function(){
41338         var v = this.parseDate(this.getRawValue());
41339         if(v){
41340             this.setValue(v);
41341         }
41342     }
41343
41344     /** @cfg {Boolean} grow @hide */
41345     /** @cfg {Number} growMin @hide */
41346     /** @cfg {Number} growMax @hide */
41347     /**
41348      * @hide
41349      * @method autoSize
41350      */
41351 });/*
41352  * Based on:
41353  * Ext JS Library 1.1.1
41354  * Copyright(c) 2006-2007, Ext JS, LLC.
41355  *
41356  * Originally Released Under LGPL - original licence link has changed is not relivant.
41357  *
41358  * Fork - LGPL
41359  * <script type="text/javascript">
41360  */
41361  
41362
41363 /**
41364  * @class Roo.form.ComboBox
41365  * @extends Roo.form.TriggerField
41366  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41367  * @constructor
41368  * Create a new ComboBox.
41369  * @param {Object} config Configuration options
41370  */
41371 Roo.form.ComboBox = function(config){
41372     Roo.form.ComboBox.superclass.constructor.call(this, config);
41373     this.addEvents({
41374         /**
41375          * @event expand
41376          * Fires when the dropdown list is expanded
41377              * @param {Roo.form.ComboBox} combo This combo box
41378              */
41379         'expand' : true,
41380         /**
41381          * @event collapse
41382          * Fires when the dropdown list is collapsed
41383              * @param {Roo.form.ComboBox} combo This combo box
41384              */
41385         'collapse' : true,
41386         /**
41387          * @event beforeselect
41388          * Fires before a list item is selected. Return false to cancel the selection.
41389              * @param {Roo.form.ComboBox} combo This combo box
41390              * @param {Roo.data.Record} record The data record returned from the underlying store
41391              * @param {Number} index The index of the selected item in the dropdown list
41392              */
41393         'beforeselect' : true,
41394         /**
41395          * @event select
41396          * Fires when a list item is selected
41397              * @param {Roo.form.ComboBox} combo This combo box
41398              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41399              * @param {Number} index The index of the selected item in the dropdown list
41400              */
41401         'select' : true,
41402         /**
41403          * @event beforequery
41404          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41405          * The event object passed has these properties:
41406              * @param {Roo.form.ComboBox} combo This combo box
41407              * @param {String} query The query
41408              * @param {Boolean} forceAll true to force "all" query
41409              * @param {Boolean} cancel true to cancel the query
41410              * @param {Object} e The query event object
41411              */
41412         'beforequery': true,
41413          /**
41414          * @event add
41415          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41416              * @param {Roo.form.ComboBox} combo This combo box
41417              */
41418         'add' : true,
41419         /**
41420          * @event edit
41421          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41422              * @param {Roo.form.ComboBox} combo This combo box
41423              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41424              */
41425         'edit' : true
41426         
41427         
41428     });
41429     if(this.transform){
41430         this.allowDomMove = false;
41431         var s = Roo.getDom(this.transform);
41432         if(!this.hiddenName){
41433             this.hiddenName = s.name;
41434         }
41435         if(!this.store){
41436             this.mode = 'local';
41437             var d = [], opts = s.options;
41438             for(var i = 0, len = opts.length;i < len; i++){
41439                 var o = opts[i];
41440                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41441                 if(o.selected) {
41442                     this.value = value;
41443                 }
41444                 d.push([value, o.text]);
41445             }
41446             this.store = new Roo.data.SimpleStore({
41447                 'id': 0,
41448                 fields: ['value', 'text'],
41449                 data : d
41450             });
41451             this.valueField = 'value';
41452             this.displayField = 'text';
41453         }
41454         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41455         if(!this.lazyRender){
41456             this.target = true;
41457             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41458             s.parentNode.removeChild(s); // remove it
41459             this.render(this.el.parentNode);
41460         }else{
41461             s.parentNode.removeChild(s); // remove it
41462         }
41463
41464     }
41465     if (this.store) {
41466         this.store = Roo.factory(this.store, Roo.data);
41467     }
41468     
41469     this.selectedIndex = -1;
41470     if(this.mode == 'local'){
41471         if(config.queryDelay === undefined){
41472             this.queryDelay = 10;
41473         }
41474         if(config.minChars === undefined){
41475             this.minChars = 0;
41476         }
41477     }
41478 };
41479
41480 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41481     /**
41482      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41483      */
41484     /**
41485      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41486      * rendering into an Roo.Editor, defaults to false)
41487      */
41488     /**
41489      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41490      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41491      */
41492     /**
41493      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41494      */
41495     /**
41496      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41497      * the dropdown list (defaults to undefined, with no header element)
41498      */
41499
41500      /**
41501      * @cfg {String/Roo.Template} tpl The template to use to render the output
41502      */
41503      
41504     // private
41505     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41506     /**
41507      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41508      */
41509     listWidth: undefined,
41510     /**
41511      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41512      * mode = 'remote' or 'text' if mode = 'local')
41513      */
41514     displayField: undefined,
41515     /**
41516      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41517      * mode = 'remote' or 'value' if mode = 'local'). 
41518      * Note: use of a valueField requires the user make a selection
41519      * in order for a value to be mapped.
41520      */
41521     valueField: undefined,
41522     
41523     
41524     /**
41525      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41526      * field's data value (defaults to the underlying DOM element's name)
41527      */
41528     hiddenName: undefined,
41529     /**
41530      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41531      */
41532     listClass: '',
41533     /**
41534      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41535      */
41536     selectedClass: 'x-combo-selected',
41537     /**
41538      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41539      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41540      * which displays a downward arrow icon).
41541      */
41542     triggerClass : 'x-form-arrow-trigger',
41543     /**
41544      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41545      */
41546     shadow:'sides',
41547     /**
41548      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41549      * anchor positions (defaults to 'tl-bl')
41550      */
41551     listAlign: 'tl-bl?',
41552     /**
41553      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41554      */
41555     maxHeight: 300,
41556     /**
41557      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41558      * query specified by the allQuery config option (defaults to 'query')
41559      */
41560     triggerAction: 'query',
41561     /**
41562      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41563      * (defaults to 4, does not apply if editable = false)
41564      */
41565     minChars : 4,
41566     /**
41567      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41568      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41569      */
41570     typeAhead: false,
41571     /**
41572      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41573      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41574      */
41575     queryDelay: 500,
41576     /**
41577      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41578      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41579      */
41580     pageSize: 0,
41581     /**
41582      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41583      * when editable = true (defaults to false)
41584      */
41585     selectOnFocus:false,
41586     /**
41587      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41588      */
41589     queryParam: 'query',
41590     /**
41591      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41592      * when mode = 'remote' (defaults to 'Loading...')
41593      */
41594     loadingText: 'Loading...',
41595     /**
41596      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41597      */
41598     resizable: false,
41599     /**
41600      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41601      */
41602     handleHeight : 8,
41603     /**
41604      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41605      * traditional select (defaults to true)
41606      */
41607     editable: true,
41608     /**
41609      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41610      */
41611     allQuery: '',
41612     /**
41613      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41614      */
41615     mode: 'remote',
41616     /**
41617      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41618      * listWidth has a higher value)
41619      */
41620     minListWidth : 70,
41621     /**
41622      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41623      * allow the user to set arbitrary text into the field (defaults to false)
41624      */
41625     forceSelection:false,
41626     /**
41627      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41628      * if typeAhead = true (defaults to 250)
41629      */
41630     typeAheadDelay : 250,
41631     /**
41632      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41633      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41634      */
41635     valueNotFoundText : undefined,
41636     /**
41637      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41638      */
41639     blockFocus : false,
41640     
41641     /**
41642      * @cfg {Boolean} disableClear Disable showing of clear button.
41643      */
41644     disableClear : false,
41645     /**
41646      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41647      */
41648     alwaysQuery : false,
41649     
41650     //private
41651     addicon : false,
41652     editicon: false,
41653     
41654     // element that contains real text value.. (when hidden is used..)
41655      
41656     // private
41657     onRender : function(ct, position)
41658     {
41659         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41660         
41661         if(this.hiddenName){
41662             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41663                     'before', true);
41664             this.hiddenField.value =
41665                 this.hiddenValue !== undefined ? this.hiddenValue :
41666                 this.value !== undefined ? this.value : '';
41667
41668             // prevent input submission
41669             this.el.dom.removeAttribute('name');
41670              
41671              
41672         }
41673         
41674         if(Roo.isGecko){
41675             this.el.dom.setAttribute('autocomplete', 'off');
41676         }
41677
41678         var cls = 'x-combo-list';
41679
41680         this.list = new Roo.Layer({
41681             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41682         });
41683
41684         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41685         this.list.setWidth(lw);
41686         this.list.swallowEvent('mousewheel');
41687         this.assetHeight = 0;
41688
41689         if(this.title){
41690             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41691             this.assetHeight += this.header.getHeight();
41692         }
41693
41694         this.innerList = this.list.createChild({cls:cls+'-inner'});
41695         this.innerList.on('mouseover', this.onViewOver, this);
41696         this.innerList.on('mousemove', this.onViewMove, this);
41697         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41698         
41699         if(this.allowBlank && !this.pageSize && !this.disableClear){
41700             this.footer = this.list.createChild({cls:cls+'-ft'});
41701             this.pageTb = new Roo.Toolbar(this.footer);
41702            
41703         }
41704         if(this.pageSize){
41705             this.footer = this.list.createChild({cls:cls+'-ft'});
41706             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41707                     {pageSize: this.pageSize});
41708             
41709         }
41710         
41711         if (this.pageTb && this.allowBlank && !this.disableClear) {
41712             var _this = this;
41713             this.pageTb.add(new Roo.Toolbar.Fill(), {
41714                 cls: 'x-btn-icon x-btn-clear',
41715                 text: '&#160;',
41716                 handler: function()
41717                 {
41718                     _this.collapse();
41719                     _this.clearValue();
41720                     _this.onSelect(false, -1);
41721                 }
41722             });
41723         }
41724         if (this.footer) {
41725             this.assetHeight += this.footer.getHeight();
41726         }
41727         
41728
41729         if(!this.tpl){
41730             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41731         }
41732
41733         this.view = new Roo.View(this.innerList, this.tpl, {
41734             singleSelect:true,
41735             store: this.store,
41736             selectedClass: this.selectedClass
41737         });
41738
41739         this.view.on('click', this.onViewClick, this);
41740
41741         this.store.on('beforeload', this.onBeforeLoad, this);
41742         this.store.on('load', this.onLoad, this);
41743         this.store.on('loadexception', this.onLoadException, this);
41744
41745         if(this.resizable){
41746             this.resizer = new Roo.Resizable(this.list,  {
41747                pinned:true, handles:'se'
41748             });
41749             this.resizer.on('resize', function(r, w, h){
41750                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41751                 this.listWidth = w;
41752                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41753                 this.restrictHeight();
41754             }, this);
41755             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41756         }
41757         if(!this.editable){
41758             this.editable = true;
41759             this.setEditable(false);
41760         }  
41761         
41762         
41763         if (typeof(this.events.add.listeners) != 'undefined') {
41764             
41765             this.addicon = this.wrap.createChild(
41766                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41767        
41768             this.addicon.on('click', function(e) {
41769                 this.fireEvent('add', this);
41770             }, this);
41771         }
41772         if (typeof(this.events.edit.listeners) != 'undefined') {
41773             
41774             this.editicon = this.wrap.createChild(
41775                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41776             if (this.addicon) {
41777                 this.editicon.setStyle('margin-left', '40px');
41778             }
41779             this.editicon.on('click', function(e) {
41780                 
41781                 // we fire even  if inothing is selected..
41782                 this.fireEvent('edit', this, this.lastData );
41783                 
41784             }, this);
41785         }
41786         
41787         
41788         
41789     },
41790
41791     // private
41792     initEvents : function(){
41793         Roo.form.ComboBox.superclass.initEvents.call(this);
41794
41795         this.keyNav = new Roo.KeyNav(this.el, {
41796             "up" : function(e){
41797                 this.inKeyMode = true;
41798                 this.selectPrev();
41799             },
41800
41801             "down" : function(e){
41802                 if(!this.isExpanded()){
41803                     this.onTriggerClick();
41804                 }else{
41805                     this.inKeyMode = true;
41806                     this.selectNext();
41807                 }
41808             },
41809
41810             "enter" : function(e){
41811                 this.onViewClick();
41812                 //return true;
41813             },
41814
41815             "esc" : function(e){
41816                 this.collapse();
41817             },
41818
41819             "tab" : function(e){
41820                 this.onViewClick(false);
41821                 this.fireEvent("specialkey", this, e);
41822                 return true;
41823             },
41824
41825             scope : this,
41826
41827             doRelay : function(foo, bar, hname){
41828                 if(hname == 'down' || this.scope.isExpanded()){
41829                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41830                 }
41831                 return true;
41832             },
41833
41834             forceKeyDown: true
41835         });
41836         this.queryDelay = Math.max(this.queryDelay || 10,
41837                 this.mode == 'local' ? 10 : 250);
41838         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41839         if(this.typeAhead){
41840             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41841         }
41842         if(this.editable !== false){
41843             this.el.on("keyup", this.onKeyUp, this);
41844         }
41845         if(this.forceSelection){
41846             this.on('blur', this.doForce, this);
41847         }
41848     },
41849
41850     onDestroy : function(){
41851         if(this.view){
41852             this.view.setStore(null);
41853             this.view.el.removeAllListeners();
41854             this.view.el.remove();
41855             this.view.purgeListeners();
41856         }
41857         if(this.list){
41858             this.list.destroy();
41859         }
41860         if(this.store){
41861             this.store.un('beforeload', this.onBeforeLoad, this);
41862             this.store.un('load', this.onLoad, this);
41863             this.store.un('loadexception', this.onLoadException, this);
41864         }
41865         Roo.form.ComboBox.superclass.onDestroy.call(this);
41866     },
41867
41868     // private
41869     fireKey : function(e){
41870         if(e.isNavKeyPress() && !this.list.isVisible()){
41871             this.fireEvent("specialkey", this, e);
41872         }
41873     },
41874
41875     // private
41876     onResize: function(w, h){
41877         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41878         
41879         if(typeof w != 'number'){
41880             // we do not handle it!?!?
41881             return;
41882         }
41883         var tw = this.trigger.getWidth();
41884         tw += this.addicon ? this.addicon.getWidth() : 0;
41885         tw += this.editicon ? this.editicon.getWidth() : 0;
41886         var x = w - tw;
41887         this.el.setWidth( this.adjustWidth('input', x));
41888             
41889         this.trigger.setStyle('left', x+'px');
41890         
41891         if(this.list && this.listWidth === undefined){
41892             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41893             this.list.setWidth(lw);
41894             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41895         }
41896         
41897     
41898         
41899     },
41900
41901     /**
41902      * Allow or prevent the user from directly editing the field text.  If false is passed,
41903      * the user will only be able to select from the items defined in the dropdown list.  This method
41904      * is the runtime equivalent of setting the 'editable' config option at config time.
41905      * @param {Boolean} value True to allow the user to directly edit the field text
41906      */
41907     setEditable : function(value){
41908         if(value == this.editable){
41909             return;
41910         }
41911         this.editable = value;
41912         if(!value){
41913             this.el.dom.setAttribute('readOnly', true);
41914             this.el.on('mousedown', this.onTriggerClick,  this);
41915             this.el.addClass('x-combo-noedit');
41916         }else{
41917             this.el.dom.setAttribute('readOnly', false);
41918             this.el.un('mousedown', this.onTriggerClick,  this);
41919             this.el.removeClass('x-combo-noedit');
41920         }
41921     },
41922
41923     // private
41924     onBeforeLoad : function(){
41925         if(!this.hasFocus){
41926             return;
41927         }
41928         this.innerList.update(this.loadingText ?
41929                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41930         this.restrictHeight();
41931         this.selectedIndex = -1;
41932     },
41933
41934     // private
41935     onLoad : function(){
41936         if(!this.hasFocus){
41937             return;
41938         }
41939         if(this.store.getCount() > 0){
41940             this.expand();
41941             this.restrictHeight();
41942             if(this.lastQuery == this.allQuery){
41943                 if(this.editable){
41944                     this.el.dom.select();
41945                 }
41946                 if(!this.selectByValue(this.value, true)){
41947                     this.select(0, true);
41948                 }
41949             }else{
41950                 this.selectNext();
41951                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41952                     this.taTask.delay(this.typeAheadDelay);
41953                 }
41954             }
41955         }else{
41956             this.onEmptyResults();
41957         }
41958         //this.el.focus();
41959     },
41960     // private
41961     onLoadException : function()
41962     {
41963         this.collapse();
41964         Roo.log(this.store.reader.jsonData);
41965         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41966             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41967         }
41968         
41969         
41970     },
41971     // private
41972     onTypeAhead : function(){
41973         if(this.store.getCount() > 0){
41974             var r = this.store.getAt(0);
41975             var newValue = r.data[this.displayField];
41976             var len = newValue.length;
41977             var selStart = this.getRawValue().length;
41978             if(selStart != len){
41979                 this.setRawValue(newValue);
41980                 this.selectText(selStart, newValue.length);
41981             }
41982         }
41983     },
41984
41985     // private
41986     onSelect : function(record, index){
41987         if(this.fireEvent('beforeselect', this, record, index) !== false){
41988             this.setFromData(index > -1 ? record.data : false);
41989             this.collapse();
41990             this.fireEvent('select', this, record, index);
41991         }
41992     },
41993
41994     /**
41995      * Returns the currently selected field value or empty string if no value is set.
41996      * @return {String} value The selected value
41997      */
41998     getValue : function(){
41999         if(this.valueField){
42000             return typeof this.value != 'undefined' ? this.value : '';
42001         }
42002         return Roo.form.ComboBox.superclass.getValue.call(this);
42003     },
42004
42005     /**
42006      * Clears any text/value currently set in the field
42007      */
42008     clearValue : function(){
42009         if(this.hiddenField){
42010             this.hiddenField.value = '';
42011         }
42012         this.value = '';
42013         this.setRawValue('');
42014         this.lastSelectionText = '';
42015         
42016     },
42017
42018     /**
42019      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42020      * will be displayed in the field.  If the value does not match the data value of an existing item,
42021      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42022      * Otherwise the field will be blank (although the value will still be set).
42023      * @param {String} value The value to match
42024      */
42025     setValue : function(v){
42026         var text = v;
42027         if(this.valueField){
42028             var r = this.findRecord(this.valueField, v);
42029             if(r){
42030                 text = r.data[this.displayField];
42031             }else if(this.valueNotFoundText !== undefined){
42032                 text = this.valueNotFoundText;
42033             }
42034         }
42035         this.lastSelectionText = text;
42036         if(this.hiddenField){
42037             this.hiddenField.value = v;
42038         }
42039         Roo.form.ComboBox.superclass.setValue.call(this, text);
42040         this.value = v;
42041     },
42042     /**
42043      * @property {Object} the last set data for the element
42044      */
42045     
42046     lastData : false,
42047     /**
42048      * Sets the value of the field based on a object which is related to the record format for the store.
42049      * @param {Object} value the value to set as. or false on reset?
42050      */
42051     setFromData : function(o){
42052         var dv = ''; // display value
42053         var vv = ''; // value value..
42054         this.lastData = o;
42055         if (this.displayField) {
42056             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42057         } else {
42058             // this is an error condition!!!
42059             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42060         }
42061         
42062         if(this.valueField){
42063             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42064         }
42065         if(this.hiddenField){
42066             this.hiddenField.value = vv;
42067             
42068             this.lastSelectionText = dv;
42069             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42070             this.value = vv;
42071             return;
42072         }
42073         // no hidden field.. - we store the value in 'value', but still display
42074         // display field!!!!
42075         this.lastSelectionText = dv;
42076         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42077         this.value = vv;
42078         
42079         
42080     },
42081     // private
42082     reset : function(){
42083         // overridden so that last data is reset..
42084         this.setValue(this.resetValue);
42085         this.originalValue = this.getValue();
42086         this.clearInvalid();
42087         this.lastData = false;
42088         if (this.view) {
42089             this.view.clearSelections();
42090         }
42091     },
42092     // private
42093     findRecord : function(prop, value){
42094         var record;
42095         if(this.store.getCount() > 0){
42096             this.store.each(function(r){
42097                 if(r.data[prop] == value){
42098                     record = r;
42099                     return false;
42100                 }
42101                 return true;
42102             });
42103         }
42104         return record;
42105     },
42106     
42107     getName: function()
42108     {
42109         // returns hidden if it's set..
42110         if (!this.rendered) {return ''};
42111         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42112         
42113     },
42114     // private
42115     onViewMove : function(e, t){
42116         this.inKeyMode = false;
42117     },
42118
42119     // private
42120     onViewOver : function(e, t){
42121         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42122             return;
42123         }
42124         var item = this.view.findItemFromChild(t);
42125         if(item){
42126             var index = this.view.indexOf(item);
42127             this.select(index, false);
42128         }
42129     },
42130
42131     // private
42132     onViewClick : function(doFocus)
42133     {
42134         var index = this.view.getSelectedIndexes()[0];
42135         var r = this.store.getAt(index);
42136         if(r){
42137             this.onSelect(r, index);
42138         }
42139         if(doFocus !== false && !this.blockFocus){
42140             this.el.focus();
42141         }
42142     },
42143
42144     // private
42145     restrictHeight : function(){
42146         this.innerList.dom.style.height = '';
42147         var inner = this.innerList.dom;
42148         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42149         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42150         this.list.beginUpdate();
42151         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42152         this.list.alignTo(this.el, this.listAlign);
42153         this.list.endUpdate();
42154     },
42155
42156     // private
42157     onEmptyResults : function(){
42158         this.collapse();
42159     },
42160
42161     /**
42162      * Returns true if the dropdown list is expanded, else false.
42163      */
42164     isExpanded : function(){
42165         return this.list.isVisible();
42166     },
42167
42168     /**
42169      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42170      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42171      * @param {String} value The data value of the item to select
42172      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42173      * selected item if it is not currently in view (defaults to true)
42174      * @return {Boolean} True if the value matched an item in the list, else false
42175      */
42176     selectByValue : function(v, scrollIntoView){
42177         if(v !== undefined && v !== null){
42178             var r = this.findRecord(this.valueField || this.displayField, v);
42179             if(r){
42180                 this.select(this.store.indexOf(r), scrollIntoView);
42181                 return true;
42182             }
42183         }
42184         return false;
42185     },
42186
42187     /**
42188      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42189      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42190      * @param {Number} index The zero-based index of the list item to select
42191      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42192      * selected item if it is not currently in view (defaults to true)
42193      */
42194     select : function(index, scrollIntoView){
42195         this.selectedIndex = index;
42196         this.view.select(index);
42197         if(scrollIntoView !== false){
42198             var el = this.view.getNode(index);
42199             if(el){
42200                 this.innerList.scrollChildIntoView(el, false);
42201             }
42202         }
42203     },
42204
42205     // private
42206     selectNext : function(){
42207         var ct = this.store.getCount();
42208         if(ct > 0){
42209             if(this.selectedIndex == -1){
42210                 this.select(0);
42211             }else if(this.selectedIndex < ct-1){
42212                 this.select(this.selectedIndex+1);
42213             }
42214         }
42215     },
42216
42217     // private
42218     selectPrev : function(){
42219         var ct = this.store.getCount();
42220         if(ct > 0){
42221             if(this.selectedIndex == -1){
42222                 this.select(0);
42223             }else if(this.selectedIndex != 0){
42224                 this.select(this.selectedIndex-1);
42225             }
42226         }
42227     },
42228
42229     // private
42230     onKeyUp : function(e){
42231         if(this.editable !== false && !e.isSpecialKey()){
42232             this.lastKey = e.getKey();
42233             this.dqTask.delay(this.queryDelay);
42234         }
42235     },
42236
42237     // private
42238     validateBlur : function(){
42239         return !this.list || !this.list.isVisible();   
42240     },
42241
42242     // private
42243     initQuery : function(){
42244         this.doQuery(this.getRawValue());
42245     },
42246
42247     // private
42248     doForce : function(){
42249         if(this.el.dom.value.length > 0){
42250             this.el.dom.value =
42251                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42252              
42253         }
42254     },
42255
42256     /**
42257      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42258      * query allowing the query action to be canceled if needed.
42259      * @param {String} query The SQL query to execute
42260      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42261      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42262      * saved in the current store (defaults to false)
42263      */
42264     doQuery : function(q, forceAll){
42265         if(q === undefined || q === null){
42266             q = '';
42267         }
42268         var qe = {
42269             query: q,
42270             forceAll: forceAll,
42271             combo: this,
42272             cancel:false
42273         };
42274         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42275             return false;
42276         }
42277         q = qe.query;
42278         forceAll = qe.forceAll;
42279         if(forceAll === true || (q.length >= this.minChars)){
42280             if(this.lastQuery != q || this.alwaysQuery){
42281                 this.lastQuery = q;
42282                 if(this.mode == 'local'){
42283                     this.selectedIndex = -1;
42284                     if(forceAll){
42285                         this.store.clearFilter();
42286                     }else{
42287                         this.store.filter(this.displayField, q);
42288                     }
42289                     this.onLoad();
42290                 }else{
42291                     this.store.baseParams[this.queryParam] = q;
42292                     this.store.load({
42293                         params: this.getParams(q)
42294                     });
42295                     this.expand();
42296                 }
42297             }else{
42298                 this.selectedIndex = -1;
42299                 this.onLoad();   
42300             }
42301         }
42302     },
42303
42304     // private
42305     getParams : function(q){
42306         var p = {};
42307         //p[this.queryParam] = q;
42308         if(this.pageSize){
42309             p.start = 0;
42310             p.limit = this.pageSize;
42311         }
42312         return p;
42313     },
42314
42315     /**
42316      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42317      */
42318     collapse : function(){
42319         if(!this.isExpanded()){
42320             return;
42321         }
42322         this.list.hide();
42323         Roo.get(document).un('mousedown', this.collapseIf, this);
42324         Roo.get(document).un('mousewheel', this.collapseIf, this);
42325         if (!this.editable) {
42326             Roo.get(document).un('keydown', this.listKeyPress, this);
42327         }
42328         this.fireEvent('collapse', this);
42329     },
42330
42331     // private
42332     collapseIf : function(e){
42333         if(!e.within(this.wrap) && !e.within(this.list)){
42334             this.collapse();
42335         }
42336     },
42337
42338     /**
42339      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42340      */
42341     expand : function(){
42342         if(this.isExpanded() || !this.hasFocus){
42343             return;
42344         }
42345         this.list.alignTo(this.el, this.listAlign);
42346         this.list.show();
42347         Roo.get(document).on('mousedown', this.collapseIf, this);
42348         Roo.get(document).on('mousewheel', this.collapseIf, this);
42349         if (!this.editable) {
42350             Roo.get(document).on('keydown', this.listKeyPress, this);
42351         }
42352         
42353         this.fireEvent('expand', this);
42354     },
42355
42356     // private
42357     // Implements the default empty TriggerField.onTriggerClick function
42358     onTriggerClick : function(){
42359         if(this.disabled){
42360             return;
42361         }
42362         if(this.isExpanded()){
42363             this.collapse();
42364             if (!this.blockFocus) {
42365                 this.el.focus();
42366             }
42367             
42368         }else {
42369             this.hasFocus = true;
42370             if(this.triggerAction == 'all') {
42371                 this.doQuery(this.allQuery, true);
42372             } else {
42373                 this.doQuery(this.getRawValue());
42374             }
42375             if (!this.blockFocus) {
42376                 this.el.focus();
42377             }
42378         }
42379     },
42380     listKeyPress : function(e)
42381     {
42382         //Roo.log('listkeypress');
42383         // scroll to first matching element based on key pres..
42384         if (e.isSpecialKey()) {
42385             return false;
42386         }
42387         var k = String.fromCharCode(e.getKey()).toUpperCase();
42388         //Roo.log(k);
42389         var match  = false;
42390         var csel = this.view.getSelectedNodes();
42391         var cselitem = false;
42392         if (csel.length) {
42393             var ix = this.view.indexOf(csel[0]);
42394             cselitem  = this.store.getAt(ix);
42395             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42396                 cselitem = false;
42397             }
42398             
42399         }
42400         
42401         this.store.each(function(v) { 
42402             if (cselitem) {
42403                 // start at existing selection.
42404                 if (cselitem.id == v.id) {
42405                     cselitem = false;
42406                 }
42407                 return;
42408             }
42409                 
42410             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42411                 match = this.store.indexOf(v);
42412                 return false;
42413             }
42414         }, this);
42415         
42416         if (match === false) {
42417             return true; // no more action?
42418         }
42419         // scroll to?
42420         this.view.select(match);
42421         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42422         sn.scrollIntoView(sn.dom.parentNode, false);
42423     } 
42424
42425     /** 
42426     * @cfg {Boolean} grow 
42427     * @hide 
42428     */
42429     /** 
42430     * @cfg {Number} growMin 
42431     * @hide 
42432     */
42433     /** 
42434     * @cfg {Number} growMax 
42435     * @hide 
42436     */
42437     /**
42438      * @hide
42439      * @method autoSize
42440      */
42441 });/*
42442  * Copyright(c) 2010-2012, Roo J Solutions Limited
42443  *
42444  * Licence LGPL
42445  *
42446  */
42447
42448 /**
42449  * @class Roo.form.ComboBoxArray
42450  * @extends Roo.form.TextField
42451  * A facebook style adder... for lists of email / people / countries  etc...
42452  * pick multiple items from a combo box, and shows each one.
42453  *
42454  *  Fred [x]  Brian [x]  [Pick another |v]
42455  *
42456  *
42457  *  For this to work: it needs various extra information
42458  *    - normal combo problay has
42459  *      name, hiddenName
42460  *    + displayField, valueField
42461  *
42462  *    For our purpose...
42463  *
42464  *
42465  *   If we change from 'extends' to wrapping...
42466  *   
42467  *  
42468  *
42469  
42470  
42471  * @constructor
42472  * Create a new ComboBoxArray.
42473  * @param {Object} config Configuration options
42474  */
42475  
42476
42477 Roo.form.ComboBoxArray = function(config)
42478 {
42479     this.addEvents({
42480         /**
42481          * @event beforeremove
42482          * Fires before remove the value from the list
42483              * @param {Roo.form.ComboBoxArray} _self This combo box array
42484              * @param {Roo.form.ComboBoxArray.Item} item removed item
42485              */
42486         'beforeremove' : true,
42487         /**
42488          * @event remove
42489          * Fires when remove the value from the list
42490              * @param {Roo.form.ComboBoxArray} _self This combo box array
42491              * @param {Roo.form.ComboBoxArray.Item} item removed item
42492              */
42493         'remove' : true
42494         
42495         
42496     });
42497     
42498     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42499     
42500     this.items = new Roo.util.MixedCollection(false);
42501     
42502     // construct the child combo...
42503     
42504     
42505     
42506     
42507    
42508     
42509 }
42510
42511  
42512 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42513
42514     /**
42515      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42516      */
42517     
42518     lastData : false,
42519     
42520     // behavies liek a hiddne field
42521     inputType:      'hidden',
42522     /**
42523      * @cfg {Number} width The width of the box that displays the selected element
42524      */ 
42525     width:          300,
42526
42527     
42528     
42529     /**
42530      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42531      */
42532     name : false,
42533     /**
42534      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42535      */
42536     hiddenName : false,
42537       /**
42538      * @cfg {String} seperator    The value seperator normally ',' 
42539      */
42540     seperator : ',',
42541     
42542     // private the array of items that are displayed..
42543     items  : false,
42544     // private - the hidden field el.
42545     hiddenEl : false,
42546     // private - the filed el..
42547     el : false,
42548     
42549     //validateValue : function() { return true; }, // all values are ok!
42550     //onAddClick: function() { },
42551     
42552     onRender : function(ct, position) 
42553     {
42554         
42555         // create the standard hidden element
42556         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42557         
42558         
42559         // give fake names to child combo;
42560         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42561         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42562         
42563         this.combo = Roo.factory(this.combo, Roo.form);
42564         this.combo.onRender(ct, position);
42565         if (typeof(this.combo.width) != 'undefined') {
42566             this.combo.onResize(this.combo.width,0);
42567         }
42568         
42569         this.combo.initEvents();
42570         
42571         // assigned so form know we need to do this..
42572         this.store          = this.combo.store;
42573         this.valueField     = this.combo.valueField;
42574         this.displayField   = this.combo.displayField ;
42575         
42576         
42577         this.combo.wrap.addClass('x-cbarray-grp');
42578         
42579         var cbwrap = this.combo.wrap.createChild(
42580             {tag: 'div', cls: 'x-cbarray-cb'},
42581             this.combo.el.dom
42582         );
42583         
42584              
42585         this.hiddenEl = this.combo.wrap.createChild({
42586             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42587         });
42588         this.el = this.combo.wrap.createChild({
42589             tag: 'input',  type:'hidden' , name: this.name, value : ''
42590         });
42591          //   this.el.dom.removeAttribute("name");
42592         
42593         
42594         this.outerWrap = this.combo.wrap;
42595         this.wrap = cbwrap;
42596         
42597         this.outerWrap.setWidth(this.width);
42598         this.outerWrap.dom.removeChild(this.el.dom);
42599         
42600         this.wrap.dom.appendChild(this.el.dom);
42601         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42602         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42603         
42604         this.combo.trigger.setStyle('position','relative');
42605         this.combo.trigger.setStyle('left', '0px');
42606         this.combo.trigger.setStyle('top', '2px');
42607         
42608         this.combo.el.setStyle('vertical-align', 'text-bottom');
42609         
42610         //this.trigger.setStyle('vertical-align', 'top');
42611         
42612         // this should use the code from combo really... on('add' ....)
42613         if (this.adder) {
42614             
42615         
42616             this.adder = this.outerWrap.createChild(
42617                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42618             var _t = this;
42619             this.adder.on('click', function(e) {
42620                 _t.fireEvent('adderclick', this, e);
42621             }, _t);
42622         }
42623         //var _t = this;
42624         //this.adder.on('click', this.onAddClick, _t);
42625         
42626         
42627         this.combo.on('select', function(cb, rec, ix) {
42628             this.addItem(rec.data);
42629             
42630             cb.setValue('');
42631             cb.el.dom.value = '';
42632             //cb.lastData = rec.data;
42633             // add to list
42634             
42635         }, this);
42636         
42637         
42638     },
42639     
42640     
42641     getName: function()
42642     {
42643         // returns hidden if it's set..
42644         if (!this.rendered) {return ''};
42645         return  this.hiddenName ? this.hiddenName : this.name;
42646         
42647     },
42648     
42649     
42650     onResize: function(w, h){
42651         
42652         return;
42653         // not sure if this is needed..
42654         //this.combo.onResize(w,h);
42655         
42656         if(typeof w != 'number'){
42657             // we do not handle it!?!?
42658             return;
42659         }
42660         var tw = this.combo.trigger.getWidth();
42661         tw += this.addicon ? this.addicon.getWidth() : 0;
42662         tw += this.editicon ? this.editicon.getWidth() : 0;
42663         var x = w - tw;
42664         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42665             
42666         this.combo.trigger.setStyle('left', '0px');
42667         
42668         if(this.list && this.listWidth === undefined){
42669             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42670             this.list.setWidth(lw);
42671             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42672         }
42673         
42674     
42675         
42676     },
42677     
42678     addItem: function(rec)
42679     {
42680         var valueField = this.combo.valueField;
42681         var displayField = this.combo.displayField;
42682         
42683         if (this.items.indexOfKey(rec[valueField]) > -1) {
42684             //console.log("GOT " + rec.data.id);
42685             return;
42686         }
42687         
42688         var x = new Roo.form.ComboBoxArray.Item({
42689             //id : rec[this.idField],
42690             data : rec,
42691             displayField : displayField ,
42692             tipField : displayField ,
42693             cb : this
42694         });
42695         // use the 
42696         this.items.add(rec[valueField],x);
42697         // add it before the element..
42698         this.updateHiddenEl();
42699         x.render(this.outerWrap, this.wrap.dom);
42700         // add the image handler..
42701     },
42702     
42703     updateHiddenEl : function()
42704     {
42705         this.validate();
42706         if (!this.hiddenEl) {
42707             return;
42708         }
42709         var ar = [];
42710         var idField = this.combo.valueField;
42711         
42712         this.items.each(function(f) {
42713             ar.push(f.data[idField]);
42714         });
42715         this.hiddenEl.dom.value = ar.join(this.seperator);
42716         this.validate();
42717     },
42718     
42719     reset : function()
42720     {
42721         this.items.clear();
42722         
42723         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42724            el.remove();
42725         });
42726         
42727         this.el.dom.value = '';
42728         if (this.hiddenEl) {
42729             this.hiddenEl.dom.value = '';
42730         }
42731         
42732     },
42733     getValue: function()
42734     {
42735         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42736     },
42737     setValue: function(v) // not a valid action - must use addItems..
42738     {
42739         
42740         this.reset();
42741          
42742         if (this.store.isLocal && (typeof(v) == 'string')) {
42743             // then we can use the store to find the values..
42744             // comma seperated at present.. this needs to allow JSON based encoding..
42745             this.hiddenEl.value  = v;
42746             var v_ar = [];
42747             Roo.each(v.split(this.seperator), function(k) {
42748                 Roo.log("CHECK " + this.valueField + ',' + k);
42749                 var li = this.store.query(this.valueField, k);
42750                 if (!li.length) {
42751                     return;
42752                 }
42753                 var add = {};
42754                 add[this.valueField] = k;
42755                 add[this.displayField] = li.item(0).data[this.displayField];
42756                 
42757                 this.addItem(add);
42758             }, this) 
42759              
42760         }
42761         if (typeof(v) == 'object' ) {
42762             // then let's assume it's an array of objects..
42763             Roo.each(v, function(l) {
42764                 var add = l;
42765                 if (typeof(l) == 'string') {
42766                     add = {};
42767                     add[this.valueField] = l;
42768                     add[this.displayField] = l
42769                 }
42770                 this.addItem(add);
42771             }, this);
42772              
42773         }
42774         
42775         
42776     },
42777     setFromData: function(v)
42778     {
42779         // this recieves an object, if setValues is called.
42780         this.reset();
42781         this.el.dom.value = v[this.displayField];
42782         this.hiddenEl.dom.value = v[this.valueField];
42783         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42784             return;
42785         }
42786         var kv = v[this.valueField];
42787         var dv = v[this.displayField];
42788         kv = typeof(kv) != 'string' ? '' : kv;
42789         dv = typeof(dv) != 'string' ? '' : dv;
42790         
42791         
42792         var keys = kv.split(this.seperator);
42793         var display = dv.split(this.seperator);
42794         for (var i = 0 ; i < keys.length; i++) {
42795             add = {};
42796             add[this.valueField] = keys[i];
42797             add[this.displayField] = display[i];
42798             this.addItem(add);
42799         }
42800       
42801         
42802     },
42803     
42804     /**
42805      * Validates the combox array value
42806      * @return {Boolean} True if the value is valid, else false
42807      */
42808     validate : function(){
42809         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42810             this.clearInvalid();
42811             return true;
42812         }
42813         return false;
42814     },
42815     
42816     validateValue : function(value){
42817         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42818         
42819     },
42820     
42821     /*@
42822      * overide
42823      * 
42824      */
42825     isDirty : function() {
42826         if(this.disabled) {
42827             return false;
42828         }
42829         
42830         try {
42831             var d = Roo.decode(String(this.originalValue));
42832         } catch (e) {
42833             return String(this.getValue()) !== String(this.originalValue);
42834         }
42835         
42836         var originalValue = [];
42837         
42838         for (var i = 0; i < d.length; i++){
42839             originalValue.push(d[i][this.valueField]);
42840         }
42841         
42842         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42843         
42844     }
42845     
42846 });
42847
42848
42849
42850 /**
42851  * @class Roo.form.ComboBoxArray.Item
42852  * @extends Roo.BoxComponent
42853  * A selected item in the list
42854  *  Fred [x]  Brian [x]  [Pick another |v]
42855  * 
42856  * @constructor
42857  * Create a new item.
42858  * @param {Object} config Configuration options
42859  */
42860  
42861 Roo.form.ComboBoxArray.Item = function(config) {
42862     config.id = Roo.id();
42863     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42864 }
42865
42866 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42867     data : {},
42868     cb: false,
42869     displayField : false,
42870     tipField : false,
42871     
42872     
42873     defaultAutoCreate : {
42874         tag: 'div',
42875         cls: 'x-cbarray-item',
42876         cn : [ 
42877             { tag: 'div' },
42878             {
42879                 tag: 'img',
42880                 width:16,
42881                 height : 16,
42882                 src : Roo.BLANK_IMAGE_URL ,
42883                 align: 'center'
42884             }
42885         ]
42886         
42887     },
42888     
42889  
42890     onRender : function(ct, position)
42891     {
42892         Roo.form.Field.superclass.onRender.call(this, ct, position);
42893         
42894         if(!this.el){
42895             var cfg = this.getAutoCreate();
42896             this.el = ct.createChild(cfg, position);
42897         }
42898         
42899         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42900         
42901         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42902             this.cb.renderer(this.data) :
42903             String.format('{0}',this.data[this.displayField]);
42904         
42905             
42906         this.el.child('div').dom.setAttribute('qtip',
42907                         String.format('{0}',this.data[this.tipField])
42908         );
42909         
42910         this.el.child('img').on('click', this.remove, this);
42911         
42912     },
42913    
42914     remove : function()
42915     {
42916         if(this.cb.disabled){
42917             return;
42918         }
42919         
42920         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42921             this.cb.items.remove(this);
42922             this.el.child('img').un('click', this.remove, this);
42923             this.el.remove();
42924             this.cb.updateHiddenEl();
42925
42926             this.cb.fireEvent('remove', this.cb, this);
42927         }
42928         
42929     }
42930 });/*
42931  * RooJS Library 1.1.1
42932  * Copyright(c) 2008-2011  Alan Knowles
42933  *
42934  * License - LGPL
42935  */
42936  
42937
42938 /**
42939  * @class Roo.form.ComboNested
42940  * @extends Roo.form.ComboBox
42941  * A combobox for that allows selection of nested items in a list,
42942  * eg.
42943  *
42944  *  Book
42945  *    -> red
42946  *    -> green
42947  *  Table
42948  *    -> square
42949  *      ->red
42950  *      ->green
42951  *    -> rectangle
42952  *      ->green
42953  *      
42954  * 
42955  * @constructor
42956  * Create a new ComboNested
42957  * @param {Object} config Configuration options
42958  */
42959 Roo.form.ComboNested = function(config){
42960     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42961     // should verify some data...
42962     // like
42963     // hiddenName = required..
42964     // displayField = required
42965     // valudField == required
42966     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42967     var _t = this;
42968     Roo.each(req, function(e) {
42969         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42970             throw "Roo.form.ComboNested : missing value for: " + e;
42971         }
42972     });
42973      
42974     
42975 };
42976
42977 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42978    
42979     /*
42980      * @config {Number} max Number of columns to show
42981      */
42982     
42983     maxColumns : 3,
42984    
42985     list : null, // the outermost div..
42986     innerLists : null, // the
42987     views : null,
42988     stores : null,
42989     // private
42990     loadingChildren : false,
42991     
42992     onRender : function(ct, position)
42993     {
42994         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42995         
42996         if(this.hiddenName){
42997             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42998                     'before', true);
42999             this.hiddenField.value =
43000                 this.hiddenValue !== undefined ? this.hiddenValue :
43001                 this.value !== undefined ? this.value : '';
43002
43003             // prevent input submission
43004             this.el.dom.removeAttribute('name');
43005              
43006              
43007         }
43008         
43009         if(Roo.isGecko){
43010             this.el.dom.setAttribute('autocomplete', 'off');
43011         }
43012
43013         var cls = 'x-combo-list';
43014
43015         this.list = new Roo.Layer({
43016             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43017         });
43018
43019         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43020         this.list.setWidth(lw);
43021         this.list.swallowEvent('mousewheel');
43022         this.assetHeight = 0;
43023
43024         if(this.title){
43025             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43026             this.assetHeight += this.header.getHeight();
43027         }
43028         this.innerLists = [];
43029         this.views = [];
43030         this.stores = [];
43031         for (var i =0 ; i < this.maxColumns; i++) {
43032             this.onRenderList( cls, i);
43033         }
43034         
43035         // always needs footer, as we are going to have an 'OK' button.
43036         this.footer = this.list.createChild({cls:cls+'-ft'});
43037         this.pageTb = new Roo.Toolbar(this.footer);  
43038         var _this = this;
43039         this.pageTb.add(  {
43040             
43041             text: 'Done',
43042             handler: function()
43043             {
43044                 _this.collapse();
43045             }
43046         });
43047         
43048         if ( this.allowBlank && !this.disableClear) {
43049             
43050             this.pageTb.add(new Roo.Toolbar.Fill(), {
43051                 cls: 'x-btn-icon x-btn-clear',
43052                 text: '&#160;',
43053                 handler: function()
43054                 {
43055                     _this.collapse();
43056                     _this.clearValue();
43057                     _this.onSelect(false, -1);
43058                 }
43059             });
43060         }
43061         if (this.footer) {
43062             this.assetHeight += this.footer.getHeight();
43063         }
43064         
43065     },
43066     onRenderList : function (  cls, i)
43067     {
43068         
43069         var lw = Math.floor(
43070                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43071         );
43072         
43073         this.list.setWidth(lw); // default to '1'
43074
43075         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43076         //il.on('mouseover', this.onViewOver, this, { list:  i });
43077         //il.on('mousemove', this.onViewMove, this, { list:  i });
43078         il.setWidth(lw);
43079         il.setStyle({ 'overflow-x' : 'hidden'});
43080
43081         if(!this.tpl){
43082             this.tpl = new Roo.Template({
43083                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43084                 isEmpty: function (value, allValues) {
43085                     //Roo.log(value);
43086                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43087                     return dl ? 'has-children' : 'no-children'
43088                 }
43089             });
43090         }
43091         
43092         var store  = this.store;
43093         if (i > 0) {
43094             store  = new Roo.data.SimpleStore({
43095                 //fields : this.store.reader.meta.fields,
43096                 reader : this.store.reader,
43097                 data : [ ]
43098             });
43099         }
43100         this.stores[i]  = store;
43101                   
43102         var view = this.views[i] = new Roo.View(
43103             il,
43104             this.tpl,
43105             {
43106                 singleSelect:true,
43107                 store: store,
43108                 selectedClass: this.selectedClass
43109             }
43110         );
43111         view.getEl().setWidth(lw);
43112         view.getEl().setStyle({
43113             position: i < 1 ? 'relative' : 'absolute',
43114             top: 0,
43115             left: (i * lw ) + 'px',
43116             display : i > 0 ? 'none' : 'block'
43117         });
43118         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43119         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43120         //view.on('click', this.onViewClick, this, { list : i });
43121
43122         store.on('beforeload', this.onBeforeLoad, this);
43123         store.on('load',  this.onLoad, this, { list  : i});
43124         store.on('loadexception', this.onLoadException, this);
43125
43126         // hide the other vies..
43127         
43128         
43129         
43130     },
43131       
43132     restrictHeight : function()
43133     {
43134         var mh = 0;
43135         Roo.each(this.innerLists, function(il,i) {
43136             var el = this.views[i].getEl();
43137             el.dom.style.height = '';
43138             var inner = el.dom;
43139             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43140             // only adjust heights on other ones..
43141             mh = Math.max(h, mh);
43142             if (i < 1) {
43143                 
43144                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43145                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43146                
43147             }
43148             
43149             
43150         }, this);
43151         
43152         this.list.beginUpdate();
43153         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43154         this.list.alignTo(this.el, this.listAlign);
43155         this.list.endUpdate();
43156         
43157     },
43158      
43159     
43160     // -- store handlers..
43161     // private
43162     onBeforeLoad : function()
43163     {
43164         if(!this.hasFocus){
43165             return;
43166         }
43167         this.innerLists[0].update(this.loadingText ?
43168                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43169         this.restrictHeight();
43170         this.selectedIndex = -1;
43171     },
43172     // private
43173     onLoad : function(a,b,c,d)
43174     {
43175         if (!this.loadingChildren) {
43176             // then we are loading the top level. - hide the children
43177             for (var i = 1;i < this.views.length; i++) {
43178                 this.views[i].getEl().setStyle({ display : 'none' });
43179             }
43180             var lw = Math.floor(
43181                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43182             );
43183         
43184              this.list.setWidth(lw); // default to '1'
43185
43186             
43187         }
43188         if(!this.hasFocus){
43189             return;
43190         }
43191         
43192         if(this.store.getCount() > 0) {
43193             this.expand();
43194             this.restrictHeight();   
43195         } else {
43196             this.onEmptyResults();
43197         }
43198         
43199         if (!this.loadingChildren) {
43200             this.selectActive();
43201         }
43202         /*
43203         this.stores[1].loadData([]);
43204         this.stores[2].loadData([]);
43205         this.views
43206         */    
43207     
43208         //this.el.focus();
43209     },
43210     
43211     
43212     // private
43213     onLoadException : function()
43214     {
43215         this.collapse();
43216         Roo.log(this.store.reader.jsonData);
43217         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43218             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43219         }
43220         
43221         
43222     },
43223     // no cleaning of leading spaces on blur here.
43224     cleanLeadingSpace : function(e) { },
43225     
43226
43227     onSelectChange : function (view, sels, opts )
43228     {
43229         var ix = view.getSelectedIndexes();
43230          
43231         if (opts.list > this.maxColumns - 2) {
43232             if (view.store.getCount()<  1) {
43233                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43234
43235             } else  {
43236                 if (ix.length) {
43237                     // used to clear ?? but if we are loading unselected 
43238                     this.setFromData(view.store.getAt(ix[0]).data);
43239                 }
43240                 
43241             }
43242             
43243             return;
43244         }
43245         
43246         if (!ix.length) {
43247             // this get's fired when trigger opens..
43248            // this.setFromData({});
43249             var str = this.stores[opts.list+1];
43250             str.data.clear(); // removeall wihtout the fire events..
43251             return;
43252         }
43253         
43254         var rec = view.store.getAt(ix[0]);
43255          
43256         this.setFromData(rec.data);
43257         this.fireEvent('select', this, rec, ix[0]);
43258         
43259         var lw = Math.floor(
43260              (
43261                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43262              ) / this.maxColumns
43263         );
43264         this.loadingChildren = true;
43265         this.stores[opts.list+1].loadDataFromChildren( rec );
43266         this.loadingChildren = false;
43267         var dl = this.stores[opts.list+1]. getTotalCount();
43268         
43269         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43270         
43271         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43272         for (var i = opts.list+2; i < this.views.length;i++) {
43273             this.views[i].getEl().setStyle({ display : 'none' });
43274         }
43275         
43276         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43277         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43278         
43279         if (this.isLoading) {
43280            // this.selectActive(opts.list);
43281         }
43282          
43283     },
43284     
43285     
43286     
43287     
43288     onDoubleClick : function()
43289     {
43290         this.collapse(); //??
43291     },
43292     
43293      
43294     
43295     
43296     
43297     // private
43298     recordToStack : function(store, prop, value, stack)
43299     {
43300         var cstore = new Roo.data.SimpleStore({
43301             //fields : this.store.reader.meta.fields, // we need array reader.. for
43302             reader : this.store.reader,
43303             data : [ ]
43304         });
43305         var _this = this;
43306         var record  = false;
43307         var srec = false;
43308         if(store.getCount() < 1){
43309             return false;
43310         }
43311         store.each(function(r){
43312             if(r.data[prop] == value){
43313                 record = r;
43314             srec = r;
43315                 return false;
43316             }
43317             if (r.data.cn && r.data.cn.length) {
43318                 cstore.loadDataFromChildren( r);
43319                 var cret = _this.recordToStack(cstore, prop, value, stack);
43320                 if (cret !== false) {
43321                     record = cret;
43322                     srec = r;
43323                     return false;
43324                 }
43325             }
43326              
43327             return true;
43328         });
43329         if (record == false) {
43330             return false
43331         }
43332         stack.unshift(srec);
43333         return record;
43334     },
43335     
43336     /*
43337      * find the stack of stores that match our value.
43338      *
43339      * 
43340      */
43341     
43342     selectActive : function ()
43343     {
43344         // if store is not loaded, then we will need to wait for that to happen first.
43345         var stack = [];
43346         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43347         for (var i = 0; i < stack.length; i++ ) {
43348             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43349         }
43350         
43351     }
43352         
43353          
43354     
43355     
43356     
43357     
43358 });/*
43359  * Based on:
43360  * Ext JS Library 1.1.1
43361  * Copyright(c) 2006-2007, Ext JS, LLC.
43362  *
43363  * Originally Released Under LGPL - original licence link has changed is not relivant.
43364  *
43365  * Fork - LGPL
43366  * <script type="text/javascript">
43367  */
43368 /**
43369  * @class Roo.form.Checkbox
43370  * @extends Roo.form.Field
43371  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43372  * @constructor
43373  * Creates a new Checkbox
43374  * @param {Object} config Configuration options
43375  */
43376 Roo.form.Checkbox = function(config){
43377     Roo.form.Checkbox.superclass.constructor.call(this, config);
43378     this.addEvents({
43379         /**
43380          * @event check
43381          * Fires when the checkbox is checked or unchecked.
43382              * @param {Roo.form.Checkbox} this This checkbox
43383              * @param {Boolean} checked The new checked value
43384              */
43385         check : true
43386     });
43387 };
43388
43389 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43390     /**
43391      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43392      */
43393     focusClass : undefined,
43394     /**
43395      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43396      */
43397     fieldClass: "x-form-field",
43398     /**
43399      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43400      */
43401     checked: false,
43402     /**
43403      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43404      * {tag: "input", type: "checkbox", autocomplete: "off"})
43405      */
43406     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43407     /**
43408      * @cfg {String} boxLabel The text that appears beside the checkbox
43409      */
43410     boxLabel : "",
43411     /**
43412      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43413      */  
43414     inputValue : '1',
43415     /**
43416      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43417      */
43418      valueOff: '0', // value when not checked..
43419
43420     actionMode : 'viewEl', 
43421     //
43422     // private
43423     itemCls : 'x-menu-check-item x-form-item',
43424     groupClass : 'x-menu-group-item',
43425     inputType : 'hidden',
43426     
43427     
43428     inSetChecked: false, // check that we are not calling self...
43429     
43430     inputElement: false, // real input element?
43431     basedOn: false, // ????
43432     
43433     isFormField: true, // not sure where this is needed!!!!
43434
43435     onResize : function(){
43436         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43437         if(!this.boxLabel){
43438             this.el.alignTo(this.wrap, 'c-c');
43439         }
43440     },
43441
43442     initEvents : function(){
43443         Roo.form.Checkbox.superclass.initEvents.call(this);
43444         this.el.on("click", this.onClick,  this);
43445         this.el.on("change", this.onClick,  this);
43446     },
43447
43448
43449     getResizeEl : function(){
43450         return this.wrap;
43451     },
43452
43453     getPositionEl : function(){
43454         return this.wrap;
43455     },
43456
43457     // private
43458     onRender : function(ct, position){
43459         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43460         /*
43461         if(this.inputValue !== undefined){
43462             this.el.dom.value = this.inputValue;
43463         }
43464         */
43465         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43466         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43467         var viewEl = this.wrap.createChild({ 
43468             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43469         this.viewEl = viewEl;   
43470         this.wrap.on('click', this.onClick,  this); 
43471         
43472         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43473         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43474         
43475         
43476         
43477         if(this.boxLabel){
43478             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43479         //    viewEl.on('click', this.onClick,  this); 
43480         }
43481         //if(this.checked){
43482             this.setChecked(this.checked);
43483         //}else{
43484             //this.checked = this.el.dom;
43485         //}
43486
43487     },
43488
43489     // private
43490     initValue : Roo.emptyFn,
43491
43492     /**
43493      * Returns the checked state of the checkbox.
43494      * @return {Boolean} True if checked, else false
43495      */
43496     getValue : function(){
43497         if(this.el){
43498             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43499         }
43500         return this.valueOff;
43501         
43502     },
43503
43504         // private
43505     onClick : function(){ 
43506         if (this.disabled) {
43507             return;
43508         }
43509         this.setChecked(!this.checked);
43510
43511         //if(this.el.dom.checked != this.checked){
43512         //    this.setValue(this.el.dom.checked);
43513        // }
43514     },
43515
43516     /**
43517      * Sets the checked state of the checkbox.
43518      * On is always based on a string comparison between inputValue and the param.
43519      * @param {Boolean/String} value - the value to set 
43520      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43521      */
43522     setValue : function(v,suppressEvent){
43523         
43524         
43525         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43526         //if(this.el && this.el.dom){
43527         //    this.el.dom.checked = this.checked;
43528         //    this.el.dom.defaultChecked = this.checked;
43529         //}
43530         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43531         //this.fireEvent("check", this, this.checked);
43532     },
43533     // private..
43534     setChecked : function(state,suppressEvent)
43535     {
43536         if (this.inSetChecked) {
43537             this.checked = state;
43538             return;
43539         }
43540         
43541     
43542         if(this.wrap){
43543             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43544         }
43545         this.checked = state;
43546         if(suppressEvent !== true){
43547             this.fireEvent('check', this, state);
43548         }
43549         this.inSetChecked = true;
43550         this.el.dom.value = state ? this.inputValue : this.valueOff;
43551         this.inSetChecked = false;
43552         
43553     },
43554     // handle setting of hidden value by some other method!!?!?
43555     setFromHidden: function()
43556     {
43557         if(!this.el){
43558             return;
43559         }
43560         //console.log("SET FROM HIDDEN");
43561         //alert('setFrom hidden');
43562         this.setValue(this.el.dom.value);
43563     },
43564     
43565     onDestroy : function()
43566     {
43567         if(this.viewEl){
43568             Roo.get(this.viewEl).remove();
43569         }
43570          
43571         Roo.form.Checkbox.superclass.onDestroy.call(this);
43572     },
43573     
43574     setBoxLabel : function(str)
43575     {
43576         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43577     }
43578
43579 });/*
43580  * Based on:
43581  * Ext JS Library 1.1.1
43582  * Copyright(c) 2006-2007, Ext JS, LLC.
43583  *
43584  * Originally Released Under LGPL - original licence link has changed is not relivant.
43585  *
43586  * Fork - LGPL
43587  * <script type="text/javascript">
43588  */
43589  
43590 /**
43591  * @class Roo.form.Radio
43592  * @extends Roo.form.Checkbox
43593  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43594  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43595  * @constructor
43596  * Creates a new Radio
43597  * @param {Object} config Configuration options
43598  */
43599 Roo.form.Radio = function(){
43600     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43601 };
43602 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43603     inputType: 'radio',
43604
43605     /**
43606      * If this radio is part of a group, it will return the selected value
43607      * @return {String}
43608      */
43609     getGroupValue : function(){
43610         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43611     },
43612     
43613     
43614     onRender : function(ct, position){
43615         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43616         
43617         if(this.inputValue !== undefined){
43618             this.el.dom.value = this.inputValue;
43619         }
43620          
43621         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43622         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43623         //var viewEl = this.wrap.createChild({ 
43624         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43625         //this.viewEl = viewEl;   
43626         //this.wrap.on('click', this.onClick,  this); 
43627         
43628         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43629         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43630         
43631         
43632         
43633         if(this.boxLabel){
43634             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43635         //    viewEl.on('click', this.onClick,  this); 
43636         }
43637          if(this.checked){
43638             this.el.dom.checked =   'checked' ;
43639         }
43640          
43641     } 
43642     
43643     
43644 });//<script type="text/javascript">
43645
43646 /*
43647  * Based  Ext JS Library 1.1.1
43648  * Copyright(c) 2006-2007, Ext JS, LLC.
43649  * LGPL
43650  *
43651  */
43652  
43653 /**
43654  * @class Roo.HtmlEditorCore
43655  * @extends Roo.Component
43656  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43657  *
43658  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43659  */
43660
43661 Roo.HtmlEditorCore = function(config){
43662     
43663     
43664     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43665     
43666     
43667     this.addEvents({
43668         /**
43669          * @event initialize
43670          * Fires when the editor is fully initialized (including the iframe)
43671          * @param {Roo.HtmlEditorCore} this
43672          */
43673         initialize: true,
43674         /**
43675          * @event activate
43676          * Fires when the editor is first receives the focus. Any insertion must wait
43677          * until after this event.
43678          * @param {Roo.HtmlEditorCore} this
43679          */
43680         activate: true,
43681          /**
43682          * @event beforesync
43683          * Fires before the textarea is updated with content from the editor iframe. Return false
43684          * to cancel the sync.
43685          * @param {Roo.HtmlEditorCore} this
43686          * @param {String} html
43687          */
43688         beforesync: true,
43689          /**
43690          * @event beforepush
43691          * Fires before the iframe editor is updated with content from the textarea. Return false
43692          * to cancel the push.
43693          * @param {Roo.HtmlEditorCore} this
43694          * @param {String} html
43695          */
43696         beforepush: true,
43697          /**
43698          * @event sync
43699          * Fires when the textarea is updated with content from the editor iframe.
43700          * @param {Roo.HtmlEditorCore} this
43701          * @param {String} html
43702          */
43703         sync: true,
43704          /**
43705          * @event push
43706          * Fires when the iframe editor is updated with content from the textarea.
43707          * @param {Roo.HtmlEditorCore} this
43708          * @param {String} html
43709          */
43710         push: true,
43711         
43712         /**
43713          * @event editorevent
43714          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43715          * @param {Roo.HtmlEditorCore} this
43716          */
43717         editorevent: true
43718         
43719     });
43720     
43721     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43722     
43723     // defaults : white / black...
43724     this.applyBlacklists();
43725     
43726     
43727     
43728 };
43729
43730
43731 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43732
43733
43734      /**
43735      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43736      */
43737     
43738     owner : false,
43739     
43740      /**
43741      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43742      *                        Roo.resizable.
43743      */
43744     resizable : false,
43745      /**
43746      * @cfg {Number} height (in pixels)
43747      */   
43748     height: 300,
43749    /**
43750      * @cfg {Number} width (in pixels)
43751      */   
43752     width: 500,
43753     
43754     /**
43755      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43756      * 
43757      */
43758     stylesheets: false,
43759     
43760     // id of frame..
43761     frameId: false,
43762     
43763     // private properties
43764     validationEvent : false,
43765     deferHeight: true,
43766     initialized : false,
43767     activated : false,
43768     sourceEditMode : false,
43769     onFocus : Roo.emptyFn,
43770     iframePad:3,
43771     hideMode:'offsets',
43772     
43773     clearUp: true,
43774     
43775     // blacklist + whitelisted elements..
43776     black: false,
43777     white: false,
43778      
43779     bodyCls : '',
43780
43781     /**
43782      * Protected method that will not generally be called directly. It
43783      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43784      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43785      */
43786     getDocMarkup : function(){
43787         // body styles..
43788         var st = '';
43789         
43790         // inherit styels from page...?? 
43791         if (this.stylesheets === false) {
43792             
43793             Roo.get(document.head).select('style').each(function(node) {
43794                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43795             });
43796             
43797             Roo.get(document.head).select('link').each(function(node) { 
43798                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43799             });
43800             
43801         } else if (!this.stylesheets.length) {
43802                 // simple..
43803                 st = '<style type="text/css">' +
43804                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43805                    '</style>';
43806         } else {
43807             for (var i in this.stylesheets) { 
43808                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43809             }
43810             
43811         }
43812         
43813         st +=  '<style type="text/css">' +
43814             'IMG { cursor: pointer } ' +
43815         '</style>';
43816
43817         var cls = 'roo-htmleditor-body';
43818         
43819         if(this.bodyCls.length){
43820             cls += ' ' + this.bodyCls;
43821         }
43822         
43823         return '<html><head>' + st  +
43824             //<style type="text/css">' +
43825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43826             //'</style>' +
43827             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43828     },
43829
43830     // private
43831     onRender : function(ct, position)
43832     {
43833         var _t = this;
43834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43836         
43837         
43838         this.el.dom.style.border = '0 none';
43839         this.el.dom.setAttribute('tabIndex', -1);
43840         this.el.addClass('x-hidden hide');
43841         
43842         
43843         
43844         if(Roo.isIE){ // fix IE 1px bogus margin
43845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43846         }
43847        
43848         
43849         this.frameId = Roo.id();
43850         
43851          
43852         
43853         var iframe = this.owner.wrap.createChild({
43854             tag: 'iframe',
43855             cls: 'form-control', // bootstrap..
43856             id: this.frameId,
43857             name: this.frameId,
43858             frameBorder : 'no',
43859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43860         }, this.el
43861         );
43862         
43863         
43864         this.iframe = iframe.dom;
43865
43866          this.assignDocWin();
43867         
43868         this.doc.designMode = 'on';
43869        
43870         this.doc.open();
43871         this.doc.write(this.getDocMarkup());
43872         this.doc.close();
43873
43874         
43875         var task = { // must defer to wait for browser to be ready
43876             run : function(){
43877                 //console.log("run task?" + this.doc.readyState);
43878                 this.assignDocWin();
43879                 if(this.doc.body || this.doc.readyState == 'complete'){
43880                     try {
43881                         this.doc.designMode="on";
43882                     } catch (e) {
43883                         return;
43884                     }
43885                     Roo.TaskMgr.stop(task);
43886                     this.initEditor.defer(10, this);
43887                 }
43888             },
43889             interval : 10,
43890             duration: 10000,
43891             scope: this
43892         };
43893         Roo.TaskMgr.start(task);
43894
43895     },
43896
43897     // private
43898     onResize : function(w, h)
43899     {
43900          Roo.log('resize: ' +w + ',' + h );
43901         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43902         if(!this.iframe){
43903             return;
43904         }
43905         if(typeof w == 'number'){
43906             
43907             this.iframe.style.width = w + 'px';
43908         }
43909         if(typeof h == 'number'){
43910             
43911             this.iframe.style.height = h + 'px';
43912             if(this.doc){
43913                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43914             }
43915         }
43916         
43917     },
43918
43919     /**
43920      * Toggles the editor between standard and source edit mode.
43921      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43922      */
43923     toggleSourceEdit : function(sourceEditMode){
43924         
43925         this.sourceEditMode = sourceEditMode === true;
43926         
43927         if(this.sourceEditMode){
43928  
43929             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43930             
43931         }else{
43932             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43933             //this.iframe.className = '';
43934             this.deferFocus();
43935         }
43936         //this.setSize(this.owner.wrap.getSize());
43937         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43938     },
43939
43940     
43941   
43942
43943     /**
43944      * Protected method that will not generally be called directly. If you need/want
43945      * custom HTML cleanup, this is the method you should override.
43946      * @param {String} html The HTML to be cleaned
43947      * return {String} The cleaned HTML
43948      */
43949     cleanHtml : function(html){
43950         html = String(html);
43951         if(html.length > 5){
43952             if(Roo.isSafari){ // strip safari nonsense
43953                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43954             }
43955         }
43956         if(html == '&nbsp;'){
43957             html = '';
43958         }
43959         return html;
43960     },
43961
43962     /**
43963      * HTML Editor -> Textarea
43964      * Protected method that will not generally be called directly. Syncs the contents
43965      * of the editor iframe with the textarea.
43966      */
43967     syncValue : function(){
43968         if(this.initialized){
43969             var bd = (this.doc.body || this.doc.documentElement);
43970             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43971             var html = bd.innerHTML;
43972             if(Roo.isSafari){
43973                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43974                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43975                 if(m && m[1]){
43976                     html = '<div style="'+m[0]+'">' + html + '</div>';
43977                 }
43978             }
43979             html = this.cleanHtml(html);
43980             // fix up the special chars.. normaly like back quotes in word...
43981             // however we do not want to do this with chinese..
43982             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43983                 
43984                 var cc = match.charCodeAt();
43985
43986                 // Get the character value, handling surrogate pairs
43987                 if (match.length == 2) {
43988                     // It's a surrogate pair, calculate the Unicode code point
43989                     var high = match.charCodeAt(0) - 0xD800;
43990                     var low  = match.charCodeAt(1) - 0xDC00;
43991                     cc = (high * 0x400) + low + 0x10000;
43992                 }  else if (
43993                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43994                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43995                     (cc >= 0xf900 && cc < 0xfb00 )
43996                 ) {
43997                         return match;
43998                 }  
43999          
44000                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44001                 return "&#" + cc + ";";
44002                 
44003                 
44004             });
44005             
44006             
44007              
44008             if(this.owner.fireEvent('beforesync', this, html) !== false){
44009                 this.el.dom.value = html;
44010                 this.owner.fireEvent('sync', this, html);
44011             }
44012         }
44013     },
44014
44015     /**
44016      * Protected method that will not generally be called directly. Pushes the value of the textarea
44017      * into the iframe editor.
44018      */
44019     pushValue : function(){
44020         if(this.initialized){
44021             var v = this.el.dom.value.trim();
44022             
44023 //            if(v.length < 1){
44024 //                v = '&#160;';
44025 //            }
44026             
44027             if(this.owner.fireEvent('beforepush', this, v) !== false){
44028                 var d = (this.doc.body || this.doc.documentElement);
44029                 d.innerHTML = v;
44030                 this.cleanUpPaste();
44031                 this.el.dom.value = d.innerHTML;
44032                 this.owner.fireEvent('push', this, v);
44033             }
44034         }
44035     },
44036
44037     // private
44038     deferFocus : function(){
44039         this.focus.defer(10, this);
44040     },
44041
44042     // doc'ed in Field
44043     focus : function(){
44044         if(this.win && !this.sourceEditMode){
44045             this.win.focus();
44046         }else{
44047             this.el.focus();
44048         }
44049     },
44050     
44051     assignDocWin: function()
44052     {
44053         var iframe = this.iframe;
44054         
44055          if(Roo.isIE){
44056             this.doc = iframe.contentWindow.document;
44057             this.win = iframe.contentWindow;
44058         } else {
44059 //            if (!Roo.get(this.frameId)) {
44060 //                return;
44061 //            }
44062 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44063 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44064             
44065             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44066                 return;
44067             }
44068             
44069             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44070             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44071         }
44072     },
44073     
44074     // private
44075     initEditor : function(){
44076         //console.log("INIT EDITOR");
44077         this.assignDocWin();
44078         
44079         
44080         
44081         this.doc.designMode="on";
44082         this.doc.open();
44083         this.doc.write(this.getDocMarkup());
44084         this.doc.close();
44085         
44086         var dbody = (this.doc.body || this.doc.documentElement);
44087         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44088         // this copies styles from the containing element into thsi one..
44089         // not sure why we need all of this..
44090         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44091         
44092         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44093         //ss['background-attachment'] = 'fixed'; // w3c
44094         dbody.bgProperties = 'fixed'; // ie
44095         //Roo.DomHelper.applyStyles(dbody, ss);
44096         Roo.EventManager.on(this.doc, {
44097             //'mousedown': this.onEditorEvent,
44098             'mouseup': this.onEditorEvent,
44099             'dblclick': this.onEditorEvent,
44100             'click': this.onEditorEvent,
44101             'keyup': this.onEditorEvent,
44102             buffer:100,
44103             scope: this
44104         });
44105         if(Roo.isGecko){
44106             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44107         }
44108         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44109             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44110         }
44111         this.initialized = true;
44112
44113         this.owner.fireEvent('initialize', this);
44114         this.pushValue();
44115     },
44116
44117     // private
44118     onDestroy : function(){
44119         
44120         
44121         
44122         if(this.rendered){
44123             
44124             //for (var i =0; i < this.toolbars.length;i++) {
44125             //    // fixme - ask toolbars for heights?
44126             //    this.toolbars[i].onDestroy();
44127            // }
44128             
44129             //this.wrap.dom.innerHTML = '';
44130             //this.wrap.remove();
44131         }
44132     },
44133
44134     // private
44135     onFirstFocus : function(){
44136         
44137         this.assignDocWin();
44138         
44139         
44140         this.activated = true;
44141          
44142     
44143         if(Roo.isGecko){ // prevent silly gecko errors
44144             this.win.focus();
44145             var s = this.win.getSelection();
44146             if(!s.focusNode || s.focusNode.nodeType != 3){
44147                 var r = s.getRangeAt(0);
44148                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44149                 r.collapse(true);
44150                 this.deferFocus();
44151             }
44152             try{
44153                 this.execCmd('useCSS', true);
44154                 this.execCmd('styleWithCSS', false);
44155             }catch(e){}
44156         }
44157         this.owner.fireEvent('activate', this);
44158     },
44159
44160     // private
44161     adjustFont: function(btn){
44162         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44163         //if(Roo.isSafari){ // safari
44164         //    adjust *= 2;
44165        // }
44166         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44167         if(Roo.isSafari){ // safari
44168             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44169             v =  (v < 10) ? 10 : v;
44170             v =  (v > 48) ? 48 : v;
44171             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44172             
44173         }
44174         
44175         
44176         v = Math.max(1, v+adjust);
44177         
44178         this.execCmd('FontSize', v  );
44179     },
44180
44181     onEditorEvent : function(e)
44182     {
44183         this.owner.fireEvent('editorevent', this, e);
44184       //  this.updateToolbar();
44185         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44186     },
44187
44188     insertTag : function(tg)
44189     {
44190         // could be a bit smarter... -> wrap the current selected tRoo..
44191         if (tg.toLowerCase() == 'span' ||
44192             tg.toLowerCase() == 'code' ||
44193             tg.toLowerCase() == 'sup' ||
44194             tg.toLowerCase() == 'sub' 
44195             ) {
44196             
44197             range = this.createRange(this.getSelection());
44198             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44199             wrappingNode.appendChild(range.extractContents());
44200             range.insertNode(wrappingNode);
44201
44202             return;
44203             
44204             
44205             
44206         }
44207         this.execCmd("formatblock",   tg);
44208         
44209     },
44210     
44211     insertText : function(txt)
44212     {
44213         
44214         
44215         var range = this.createRange();
44216         range.deleteContents();
44217                //alert(Sender.getAttribute('label'));
44218                
44219         range.insertNode(this.doc.createTextNode(txt));
44220     } ,
44221     
44222      
44223
44224     /**
44225      * Executes a Midas editor command on the editor document and performs necessary focus and
44226      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44227      * @param {String} cmd The Midas command
44228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44229      */
44230     relayCmd : function(cmd, value){
44231         this.win.focus();
44232         this.execCmd(cmd, value);
44233         this.owner.fireEvent('editorevent', this);
44234         //this.updateToolbar();
44235         this.owner.deferFocus();
44236     },
44237
44238     /**
44239      * Executes a Midas editor command directly on the editor document.
44240      * For visual commands, you should use {@link #relayCmd} instead.
44241      * <b>This should only be called after the editor is initialized.</b>
44242      * @param {String} cmd The Midas command
44243      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44244      */
44245     execCmd : function(cmd, value){
44246         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44247         this.syncValue();
44248     },
44249  
44250  
44251    
44252     /**
44253      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44254      * to insert tRoo.
44255      * @param {String} text | dom node.. 
44256      */
44257     insertAtCursor : function(text)
44258     {
44259         
44260         if(!this.activated){
44261             return;
44262         }
44263         /*
44264         if(Roo.isIE){
44265             this.win.focus();
44266             var r = this.doc.selection.createRange();
44267             if(r){
44268                 r.collapse(true);
44269                 r.pasteHTML(text);
44270                 this.syncValue();
44271                 this.deferFocus();
44272             
44273             }
44274             return;
44275         }
44276         */
44277         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44278             this.win.focus();
44279             
44280             
44281             // from jquery ui (MIT licenced)
44282             var range, node;
44283             var win = this.win;
44284             
44285             if (win.getSelection && win.getSelection().getRangeAt) {
44286                 range = win.getSelection().getRangeAt(0);
44287                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44288                 range.insertNode(node);
44289             } else if (win.document.selection && win.document.selection.createRange) {
44290                 // no firefox support
44291                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44292                 win.document.selection.createRange().pasteHTML(txt);
44293             } else {
44294                 // no firefox support
44295                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44296                 this.execCmd('InsertHTML', txt);
44297             } 
44298             
44299             this.syncValue();
44300             
44301             this.deferFocus();
44302         }
44303     },
44304  // private
44305     mozKeyPress : function(e){
44306         if(e.ctrlKey){
44307             var c = e.getCharCode(), cmd;
44308           
44309             if(c > 0){
44310                 c = String.fromCharCode(c).toLowerCase();
44311                 switch(c){
44312                     case 'b':
44313                         cmd = 'bold';
44314                         break;
44315                     case 'i':
44316                         cmd = 'italic';
44317                         break;
44318                     
44319                     case 'u':
44320                         cmd = 'underline';
44321                         break;
44322                     
44323                     case 'v':
44324                         this.cleanUpPaste.defer(100, this);
44325                         return;
44326                         
44327                 }
44328                 if(cmd){
44329                     this.win.focus();
44330                     this.execCmd(cmd);
44331                     this.deferFocus();
44332                     e.preventDefault();
44333                 }
44334                 
44335             }
44336         }
44337     },
44338
44339     // private
44340     fixKeys : function(){ // load time branching for fastest keydown performance
44341         if(Roo.isIE){
44342             return function(e){
44343                 var k = e.getKey(), r;
44344                 if(k == e.TAB){
44345                     e.stopEvent();
44346                     r = this.doc.selection.createRange();
44347                     if(r){
44348                         r.collapse(true);
44349                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44350                         this.deferFocus();
44351                     }
44352                     return;
44353                 }
44354                 
44355                 if(k == e.ENTER){
44356                     r = this.doc.selection.createRange();
44357                     if(r){
44358                         var target = r.parentElement();
44359                         if(!target || target.tagName.toLowerCase() != 'li'){
44360                             e.stopEvent();
44361                             r.pasteHTML('<br />');
44362                             r.collapse(false);
44363                             r.select();
44364                         }
44365                     }
44366                 }
44367                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44368                     this.cleanUpPaste.defer(100, this);
44369                     return;
44370                 }
44371                 
44372                 
44373             };
44374         }else if(Roo.isOpera){
44375             return function(e){
44376                 var k = e.getKey();
44377                 if(k == e.TAB){
44378                     e.stopEvent();
44379                     this.win.focus();
44380                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44381                     this.deferFocus();
44382                 }
44383                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44384                     this.cleanUpPaste.defer(100, this);
44385                     return;
44386                 }
44387                 
44388             };
44389         }else if(Roo.isSafari){
44390             return function(e){
44391                 var k = e.getKey();
44392                 
44393                 if(k == e.TAB){
44394                     e.stopEvent();
44395                     this.execCmd('InsertText','\t');
44396                     this.deferFocus();
44397                     return;
44398                 }
44399                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44400                     this.cleanUpPaste.defer(100, this);
44401                     return;
44402                 }
44403                 
44404              };
44405         }
44406     }(),
44407     
44408     getAllAncestors: function()
44409     {
44410         var p = this.getSelectedNode();
44411         var a = [];
44412         if (!p) {
44413             a.push(p); // push blank onto stack..
44414             p = this.getParentElement();
44415         }
44416         
44417         
44418         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44419             a.push(p);
44420             p = p.parentNode;
44421         }
44422         a.push(this.doc.body);
44423         return a;
44424     },
44425     lastSel : false,
44426     lastSelNode : false,
44427     
44428     
44429     getSelection : function() 
44430     {
44431         this.assignDocWin();
44432         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44433     },
44434     
44435     getSelectedNode: function() 
44436     {
44437         // this may only work on Gecko!!!
44438         
44439         // should we cache this!!!!
44440         
44441         
44442         
44443          
44444         var range = this.createRange(this.getSelection()).cloneRange();
44445         
44446         if (Roo.isIE) {
44447             var parent = range.parentElement();
44448             while (true) {
44449                 var testRange = range.duplicate();
44450                 testRange.moveToElementText(parent);
44451                 if (testRange.inRange(range)) {
44452                     break;
44453                 }
44454                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44455                     break;
44456                 }
44457                 parent = parent.parentElement;
44458             }
44459             return parent;
44460         }
44461         
44462         // is ancestor a text element.
44463         var ac =  range.commonAncestorContainer;
44464         if (ac.nodeType == 3) {
44465             ac = ac.parentNode;
44466         }
44467         
44468         var ar = ac.childNodes;
44469          
44470         var nodes = [];
44471         var other_nodes = [];
44472         var has_other_nodes = false;
44473         for (var i=0;i<ar.length;i++) {
44474             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44475                 continue;
44476             }
44477             // fullly contained node.
44478             
44479             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44480                 nodes.push(ar[i]);
44481                 continue;
44482             }
44483             
44484             // probably selected..
44485             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44486                 other_nodes.push(ar[i]);
44487                 continue;
44488             }
44489             // outer..
44490             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44491                 continue;
44492             }
44493             
44494             
44495             has_other_nodes = true;
44496         }
44497         if (!nodes.length && other_nodes.length) {
44498             nodes= other_nodes;
44499         }
44500         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44501             return false;
44502         }
44503         
44504         return nodes[0];
44505     },
44506     createRange: function(sel)
44507     {
44508         // this has strange effects when using with 
44509         // top toolbar - not sure if it's a great idea.
44510         //this.editor.contentWindow.focus();
44511         if (typeof sel != "undefined") {
44512             try {
44513                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44514             } catch(e) {
44515                 return this.doc.createRange();
44516             }
44517         } else {
44518             return this.doc.createRange();
44519         }
44520     },
44521     getParentElement: function()
44522     {
44523         
44524         this.assignDocWin();
44525         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44526         
44527         var range = this.createRange(sel);
44528          
44529         try {
44530             var p = range.commonAncestorContainer;
44531             while (p.nodeType == 3) { // text node
44532                 p = p.parentNode;
44533             }
44534             return p;
44535         } catch (e) {
44536             return null;
44537         }
44538     
44539     },
44540     /***
44541      *
44542      * Range intersection.. the hard stuff...
44543      *  '-1' = before
44544      *  '0' = hits..
44545      *  '1' = after.
44546      *         [ -- selected range --- ]
44547      *   [fail]                        [fail]
44548      *
44549      *    basically..
44550      *      if end is before start or  hits it. fail.
44551      *      if start is after end or hits it fail.
44552      *
44553      *   if either hits (but other is outside. - then it's not 
44554      *   
44555      *    
44556      **/
44557     
44558     
44559     // @see http://www.thismuchiknow.co.uk/?p=64.
44560     rangeIntersectsNode : function(range, node)
44561     {
44562         var nodeRange = node.ownerDocument.createRange();
44563         try {
44564             nodeRange.selectNode(node);
44565         } catch (e) {
44566             nodeRange.selectNodeContents(node);
44567         }
44568     
44569         var rangeStartRange = range.cloneRange();
44570         rangeStartRange.collapse(true);
44571     
44572         var rangeEndRange = range.cloneRange();
44573         rangeEndRange.collapse(false);
44574     
44575         var nodeStartRange = nodeRange.cloneRange();
44576         nodeStartRange.collapse(true);
44577     
44578         var nodeEndRange = nodeRange.cloneRange();
44579         nodeEndRange.collapse(false);
44580     
44581         return rangeStartRange.compareBoundaryPoints(
44582                  Range.START_TO_START, nodeEndRange) == -1 &&
44583                rangeEndRange.compareBoundaryPoints(
44584                  Range.START_TO_START, nodeStartRange) == 1;
44585         
44586          
44587     },
44588     rangeCompareNode : function(range, node)
44589     {
44590         var nodeRange = node.ownerDocument.createRange();
44591         try {
44592             nodeRange.selectNode(node);
44593         } catch (e) {
44594             nodeRange.selectNodeContents(node);
44595         }
44596         
44597         
44598         range.collapse(true);
44599     
44600         nodeRange.collapse(true);
44601      
44602         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44603         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44604          
44605         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44606         
44607         var nodeIsBefore   =  ss == 1;
44608         var nodeIsAfter    = ee == -1;
44609         
44610         if (nodeIsBefore && nodeIsAfter) {
44611             return 0; // outer
44612         }
44613         if (!nodeIsBefore && nodeIsAfter) {
44614             return 1; //right trailed.
44615         }
44616         
44617         if (nodeIsBefore && !nodeIsAfter) {
44618             return 2;  // left trailed.
44619         }
44620         // fully contined.
44621         return 3;
44622     },
44623
44624     // private? - in a new class?
44625     cleanUpPaste :  function()
44626     {
44627         // cleans up the whole document..
44628         Roo.log('cleanuppaste');
44629         
44630         this.cleanUpChildren(this.doc.body);
44631         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44632         if (clean != this.doc.body.innerHTML) {
44633             this.doc.body.innerHTML = clean;
44634         }
44635         
44636     },
44637     
44638     cleanWordChars : function(input) {// change the chars to hex code
44639         var he = Roo.HtmlEditorCore;
44640         
44641         var output = input;
44642         Roo.each(he.swapCodes, function(sw) { 
44643             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44644             
44645             output = output.replace(swapper, sw[1]);
44646         });
44647         
44648         return output;
44649     },
44650     
44651     
44652     cleanUpChildren : function (n)
44653     {
44654         if (!n.childNodes.length) {
44655             return;
44656         }
44657         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44658            this.cleanUpChild(n.childNodes[i]);
44659         }
44660     },
44661     
44662     
44663         
44664     
44665     cleanUpChild : function (node)
44666     {
44667         var ed = this;
44668         //console.log(node);
44669         if (node.nodeName == "#text") {
44670             // clean up silly Windows -- stuff?
44671             return; 
44672         }
44673         if (node.nodeName == "#comment") {
44674             node.parentNode.removeChild(node);
44675             // clean up silly Windows -- stuff?
44676             return; 
44677         }
44678         var lcname = node.tagName.toLowerCase();
44679         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44680         // whitelist of tags..
44681         
44682         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44683             // remove node.
44684             node.parentNode.removeChild(node);
44685             return;
44686             
44687         }
44688         
44689         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44690         
44691         // spans with no attributes - just remove them..
44692         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44693             remove_keep_children = true;
44694         }
44695         
44696         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44697         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44698         
44699         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44700         //    remove_keep_children = true;
44701         //}
44702         
44703         if (remove_keep_children) {
44704             this.cleanUpChildren(node);
44705             // inserts everything just before this node...
44706             while (node.childNodes.length) {
44707                 var cn = node.childNodes[0];
44708                 node.removeChild(cn);
44709                 node.parentNode.insertBefore(cn, node);
44710             }
44711             node.parentNode.removeChild(node);
44712             return;
44713         }
44714         
44715         if (!node.attributes || !node.attributes.length) {
44716             
44717           
44718             
44719             
44720             this.cleanUpChildren(node);
44721             return;
44722         }
44723         
44724         function cleanAttr(n,v)
44725         {
44726             
44727             if (v.match(/^\./) || v.match(/^\//)) {
44728                 return;
44729             }
44730             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44731                 return;
44732             }
44733             if (v.match(/^#/)) {
44734                 return;
44735             }
44736             if (v.match(/^\{/)) { // allow template editing.
44737                 return;
44738             }
44739 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44740             node.removeAttribute(n);
44741             
44742         }
44743         
44744         var cwhite = this.cwhite;
44745         var cblack = this.cblack;
44746             
44747         function cleanStyle(n,v)
44748         {
44749             if (v.match(/expression/)) { //XSS?? should we even bother..
44750                 node.removeAttribute(n);
44751                 return;
44752             }
44753             
44754             var parts = v.split(/;/);
44755             var clean = [];
44756             
44757             Roo.each(parts, function(p) {
44758                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44759                 if (!p.length) {
44760                     return true;
44761                 }
44762                 var l = p.split(':').shift().replace(/\s+/g,'');
44763                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44764                 
44765                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44766 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44767                     //node.removeAttribute(n);
44768                     return true;
44769                 }
44770                 //Roo.log()
44771                 // only allow 'c whitelisted system attributes'
44772                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44773 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44774                     //node.removeAttribute(n);
44775                     return true;
44776                 }
44777                 
44778                 
44779                  
44780                 
44781                 clean.push(p);
44782                 return true;
44783             });
44784             if (clean.length) { 
44785                 node.setAttribute(n, clean.join(';'));
44786             } else {
44787                 node.removeAttribute(n);
44788             }
44789             
44790         }
44791         
44792         
44793         for (var i = node.attributes.length-1; i > -1 ; i--) {
44794             var a = node.attributes[i];
44795             //console.log(a);
44796             
44797             if (a.name.toLowerCase().substr(0,2)=='on')  {
44798                 node.removeAttribute(a.name);
44799                 continue;
44800             }
44801             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44802                 node.removeAttribute(a.name);
44803                 continue;
44804             }
44805             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44806                 cleanAttr(a.name,a.value); // fixme..
44807                 continue;
44808             }
44809             if (a.name == 'style') {
44810                 cleanStyle(a.name,a.value);
44811                 continue;
44812             }
44813             /// clean up MS crap..
44814             // tecnically this should be a list of valid class'es..
44815             
44816             
44817             if (a.name == 'class') {
44818                 if (a.value.match(/^Mso/)) {
44819                     node.removeAttribute('class');
44820                 }
44821                 
44822                 if (a.value.match(/^body$/)) {
44823                     node.removeAttribute('class');
44824                 }
44825                 continue;
44826             }
44827             
44828             // style cleanup!?
44829             // class cleanup?
44830             
44831         }
44832         
44833         
44834         this.cleanUpChildren(node);
44835         
44836         
44837     },
44838     
44839     /**
44840      * Clean up MS wordisms...
44841      */
44842     cleanWord : function(node)
44843     {
44844         if (!node) {
44845             this.cleanWord(this.doc.body);
44846             return;
44847         }
44848         
44849         if(
44850                 node.nodeName == 'SPAN' &&
44851                 !node.hasAttributes() &&
44852                 node.childNodes.length == 1 &&
44853                 node.firstChild.nodeName == "#text"  
44854         ) {
44855             var textNode = node.firstChild;
44856             node.removeChild(textNode);
44857             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44858                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44859             }
44860             node.parentNode.insertBefore(textNode, node);
44861             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44862                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44863             }
44864             node.parentNode.removeChild(node);
44865         }
44866         
44867         if (node.nodeName == "#text") {
44868             // clean up silly Windows -- stuff?
44869             return; 
44870         }
44871         if (node.nodeName == "#comment") {
44872             node.parentNode.removeChild(node);
44873             // clean up silly Windows -- stuff?
44874             return; 
44875         }
44876         
44877         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44878             node.parentNode.removeChild(node);
44879             return;
44880         }
44881         //Roo.log(node.tagName);
44882         // remove - but keep children..
44883         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44884             //Roo.log('-- removed');
44885             while (node.childNodes.length) {
44886                 var cn = node.childNodes[0];
44887                 node.removeChild(cn);
44888                 node.parentNode.insertBefore(cn, node);
44889                 // move node to parent - and clean it..
44890                 this.cleanWord(cn);
44891             }
44892             node.parentNode.removeChild(node);
44893             /// no need to iterate chidlren = it's got none..
44894             //this.iterateChildren(node, this.cleanWord);
44895             return;
44896         }
44897         // clean styles
44898         if (node.className.length) {
44899             
44900             var cn = node.className.split(/\W+/);
44901             var cna = [];
44902             Roo.each(cn, function(cls) {
44903                 if (cls.match(/Mso[a-zA-Z]+/)) {
44904                     return;
44905                 }
44906                 cna.push(cls);
44907             });
44908             node.className = cna.length ? cna.join(' ') : '';
44909             if (!cna.length) {
44910                 node.removeAttribute("class");
44911             }
44912         }
44913         
44914         if (node.hasAttribute("lang")) {
44915             node.removeAttribute("lang");
44916         }
44917         
44918         if (node.hasAttribute("style")) {
44919             
44920             var styles = node.getAttribute("style").split(";");
44921             var nstyle = [];
44922             Roo.each(styles, function(s) {
44923                 if (!s.match(/:/)) {
44924                     return;
44925                 }
44926                 var kv = s.split(":");
44927                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44928                     return;
44929                 }
44930                 // what ever is left... we allow.
44931                 nstyle.push(s);
44932             });
44933             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44934             if (!nstyle.length) {
44935                 node.removeAttribute('style');
44936             }
44937         }
44938         this.iterateChildren(node, this.cleanWord);
44939         
44940         
44941         
44942     },
44943     /**
44944      * iterateChildren of a Node, calling fn each time, using this as the scole..
44945      * @param {DomNode} node node to iterate children of.
44946      * @param {Function} fn method of this class to call on each item.
44947      */
44948     iterateChildren : function(node, fn)
44949     {
44950         if (!node.childNodes.length) {
44951                 return;
44952         }
44953         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44954            fn.call(this, node.childNodes[i])
44955         }
44956     },
44957     
44958     
44959     /**
44960      * cleanTableWidths.
44961      *
44962      * Quite often pasting from word etc.. results in tables with column and widths.
44963      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44964      *
44965      */
44966     cleanTableWidths : function(node)
44967     {
44968          
44969          
44970         if (!node) {
44971             this.cleanTableWidths(this.doc.body);
44972             return;
44973         }
44974         
44975         // ignore list...
44976         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44977             return; 
44978         }
44979         Roo.log(node.tagName);
44980         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44981             this.iterateChildren(node, this.cleanTableWidths);
44982             return;
44983         }
44984         if (node.hasAttribute('width')) {
44985             node.removeAttribute('width');
44986         }
44987         
44988          
44989         if (node.hasAttribute("style")) {
44990             // pretty basic...
44991             
44992             var styles = node.getAttribute("style").split(";");
44993             var nstyle = [];
44994             Roo.each(styles, function(s) {
44995                 if (!s.match(/:/)) {
44996                     return;
44997                 }
44998                 var kv = s.split(":");
44999                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45000                     return;
45001                 }
45002                 // what ever is left... we allow.
45003                 nstyle.push(s);
45004             });
45005             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45006             if (!nstyle.length) {
45007                 node.removeAttribute('style');
45008             }
45009         }
45010         
45011         this.iterateChildren(node, this.cleanTableWidths);
45012         
45013         
45014     },
45015     
45016     
45017     
45018     
45019     domToHTML : function(currentElement, depth, nopadtext) {
45020         
45021         depth = depth || 0;
45022         nopadtext = nopadtext || false;
45023     
45024         if (!currentElement) {
45025             return this.domToHTML(this.doc.body);
45026         }
45027         
45028         //Roo.log(currentElement);
45029         var j;
45030         var allText = false;
45031         var nodeName = currentElement.nodeName;
45032         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45033         
45034         if  (nodeName == '#text') {
45035             
45036             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45037         }
45038         
45039         
45040         var ret = '';
45041         if (nodeName != 'BODY') {
45042              
45043             var i = 0;
45044             // Prints the node tagName, such as <A>, <IMG>, etc
45045             if (tagName) {
45046                 var attr = [];
45047                 for(i = 0; i < currentElement.attributes.length;i++) {
45048                     // quoting?
45049                     var aname = currentElement.attributes.item(i).name;
45050                     if (!currentElement.attributes.item(i).value.length) {
45051                         continue;
45052                     }
45053                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45054                 }
45055                 
45056                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45057             } 
45058             else {
45059                 
45060                 // eack
45061             }
45062         } else {
45063             tagName = false;
45064         }
45065         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45066             return ret;
45067         }
45068         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45069             nopadtext = true;
45070         }
45071         
45072         
45073         // Traverse the tree
45074         i = 0;
45075         var currentElementChild = currentElement.childNodes.item(i);
45076         var allText = true;
45077         var innerHTML  = '';
45078         lastnode = '';
45079         while (currentElementChild) {
45080             // Formatting code (indent the tree so it looks nice on the screen)
45081             var nopad = nopadtext;
45082             if (lastnode == 'SPAN') {
45083                 nopad  = true;
45084             }
45085             // text
45086             if  (currentElementChild.nodeName == '#text') {
45087                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45088                 toadd = nopadtext ? toadd : toadd.trim();
45089                 if (!nopad && toadd.length > 80) {
45090                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45091                 }
45092                 innerHTML  += toadd;
45093                 
45094                 i++;
45095                 currentElementChild = currentElement.childNodes.item(i);
45096                 lastNode = '';
45097                 continue;
45098             }
45099             allText = false;
45100             
45101             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45102                 
45103             // Recursively traverse the tree structure of the child node
45104             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45105             lastnode = currentElementChild.nodeName;
45106             i++;
45107             currentElementChild=currentElement.childNodes.item(i);
45108         }
45109         
45110         ret += innerHTML;
45111         
45112         if (!allText) {
45113                 // The remaining code is mostly for formatting the tree
45114             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45115         }
45116         
45117         
45118         if (tagName) {
45119             ret+= "</"+tagName+">";
45120         }
45121         return ret;
45122         
45123     },
45124         
45125     applyBlacklists : function()
45126     {
45127         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45128         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45129         
45130         this.white = [];
45131         this.black = [];
45132         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45133             if (b.indexOf(tag) > -1) {
45134                 return;
45135             }
45136             this.white.push(tag);
45137             
45138         }, this);
45139         
45140         Roo.each(w, function(tag) {
45141             if (b.indexOf(tag) > -1) {
45142                 return;
45143             }
45144             if (this.white.indexOf(tag) > -1) {
45145                 return;
45146             }
45147             this.white.push(tag);
45148             
45149         }, this);
45150         
45151         
45152         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45153             if (w.indexOf(tag) > -1) {
45154                 return;
45155             }
45156             this.black.push(tag);
45157             
45158         }, this);
45159         
45160         Roo.each(b, function(tag) {
45161             if (w.indexOf(tag) > -1) {
45162                 return;
45163             }
45164             if (this.black.indexOf(tag) > -1) {
45165                 return;
45166             }
45167             this.black.push(tag);
45168             
45169         }, this);
45170         
45171         
45172         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45173         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45174         
45175         this.cwhite = [];
45176         this.cblack = [];
45177         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45178             if (b.indexOf(tag) > -1) {
45179                 return;
45180             }
45181             this.cwhite.push(tag);
45182             
45183         }, this);
45184         
45185         Roo.each(w, function(tag) {
45186             if (b.indexOf(tag) > -1) {
45187                 return;
45188             }
45189             if (this.cwhite.indexOf(tag) > -1) {
45190                 return;
45191             }
45192             this.cwhite.push(tag);
45193             
45194         }, this);
45195         
45196         
45197         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45198             if (w.indexOf(tag) > -1) {
45199                 return;
45200             }
45201             this.cblack.push(tag);
45202             
45203         }, this);
45204         
45205         Roo.each(b, function(tag) {
45206             if (w.indexOf(tag) > -1) {
45207                 return;
45208             }
45209             if (this.cblack.indexOf(tag) > -1) {
45210                 return;
45211             }
45212             this.cblack.push(tag);
45213             
45214         }, this);
45215     },
45216     
45217     setStylesheets : function(stylesheets)
45218     {
45219         if(typeof(stylesheets) == 'string'){
45220             Roo.get(this.iframe.contentDocument.head).createChild({
45221                 tag : 'link',
45222                 rel : 'stylesheet',
45223                 type : 'text/css',
45224                 href : stylesheets
45225             });
45226             
45227             return;
45228         }
45229         var _this = this;
45230      
45231         Roo.each(stylesheets, function(s) {
45232             if(!s.length){
45233                 return;
45234             }
45235             
45236             Roo.get(_this.iframe.contentDocument.head).createChild({
45237                 tag : 'link',
45238                 rel : 'stylesheet',
45239                 type : 'text/css',
45240                 href : s
45241             });
45242         });
45243
45244         
45245     },
45246     
45247     removeStylesheets : function()
45248     {
45249         var _this = this;
45250         
45251         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45252             s.remove();
45253         });
45254     },
45255     
45256     setStyle : function(style)
45257     {
45258         Roo.get(this.iframe.contentDocument.head).createChild({
45259             tag : 'style',
45260             type : 'text/css',
45261             html : style
45262         });
45263
45264         return;
45265     }
45266     
45267     // hide stuff that is not compatible
45268     /**
45269      * @event blur
45270      * @hide
45271      */
45272     /**
45273      * @event change
45274      * @hide
45275      */
45276     /**
45277      * @event focus
45278      * @hide
45279      */
45280     /**
45281      * @event specialkey
45282      * @hide
45283      */
45284     /**
45285      * @cfg {String} fieldClass @hide
45286      */
45287     /**
45288      * @cfg {String} focusClass @hide
45289      */
45290     /**
45291      * @cfg {String} autoCreate @hide
45292      */
45293     /**
45294      * @cfg {String} inputType @hide
45295      */
45296     /**
45297      * @cfg {String} invalidClass @hide
45298      */
45299     /**
45300      * @cfg {String} invalidText @hide
45301      */
45302     /**
45303      * @cfg {String} msgFx @hide
45304      */
45305     /**
45306      * @cfg {String} validateOnBlur @hide
45307      */
45308 });
45309
45310 Roo.HtmlEditorCore.white = [
45311         'area', 'br', 'img', 'input', 'hr', 'wbr',
45312         
45313        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45314        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45315        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45316        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45317        'table',   'ul',         'xmp', 
45318        
45319        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45320       'thead',   'tr', 
45321      
45322       'dir', 'menu', 'ol', 'ul', 'dl',
45323        
45324       'embed',  'object'
45325 ];
45326
45327
45328 Roo.HtmlEditorCore.black = [
45329     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45330         'applet', // 
45331         'base',   'basefont', 'bgsound', 'blink',  'body', 
45332         'frame',  'frameset', 'head',    'html',   'ilayer', 
45333         'iframe', 'layer',  'link',     'meta',    'object',   
45334         'script', 'style' ,'title',  'xml' // clean later..
45335 ];
45336 Roo.HtmlEditorCore.clean = [
45337     'script', 'style', 'title', 'xml'
45338 ];
45339 Roo.HtmlEditorCore.remove = [
45340     'font'
45341 ];
45342 // attributes..
45343
45344 Roo.HtmlEditorCore.ablack = [
45345     'on'
45346 ];
45347     
45348 Roo.HtmlEditorCore.aclean = [ 
45349     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45350 ];
45351
45352 // protocols..
45353 Roo.HtmlEditorCore.pwhite= [
45354         'http',  'https',  'mailto'
45355 ];
45356
45357 // white listed style attributes.
45358 Roo.HtmlEditorCore.cwhite= [
45359       //  'text-align', /// default is to allow most things..
45360       
45361          
45362 //        'font-size'//??
45363 ];
45364
45365 // black listed style attributes.
45366 Roo.HtmlEditorCore.cblack= [
45367       //  'font-size' -- this can be set by the project 
45368 ];
45369
45370
45371 Roo.HtmlEditorCore.swapCodes   =[ 
45372     [    8211, "&#8211;" ], 
45373     [    8212, "&#8212;" ], 
45374     [    8216,  "'" ],  
45375     [    8217, "'" ],  
45376     [    8220, '"' ],  
45377     [    8221, '"' ],  
45378     [    8226, "*" ],  
45379     [    8230, "..." ]
45380 ]; 
45381
45382     //<script type="text/javascript">
45383
45384 /*
45385  * Ext JS Library 1.1.1
45386  * Copyright(c) 2006-2007, Ext JS, LLC.
45387  * Licence LGPL
45388  * 
45389  */
45390  
45391  
45392 Roo.form.HtmlEditor = function(config){
45393     
45394     
45395     
45396     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45397     
45398     if (!this.toolbars) {
45399         this.toolbars = [];
45400     }
45401     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45402     
45403     
45404 };
45405
45406 /**
45407  * @class Roo.form.HtmlEditor
45408  * @extends Roo.form.Field
45409  * Provides a lightweight HTML Editor component.
45410  *
45411  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45412  * 
45413  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45414  * supported by this editor.</b><br/><br/>
45415  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45416  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45417  */
45418 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45419     /**
45420      * @cfg {Boolean} clearUp
45421      */
45422     clearUp : true,
45423       /**
45424      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45425      */
45426     toolbars : false,
45427    
45428      /**
45429      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45430      *                        Roo.resizable.
45431      */
45432     resizable : false,
45433      /**
45434      * @cfg {Number} height (in pixels)
45435      */   
45436     height: 300,
45437    /**
45438      * @cfg {Number} width (in pixels)
45439      */   
45440     width: 500,
45441     
45442     /**
45443      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45444      * 
45445      */
45446     stylesheets: false,
45447     
45448     
45449      /**
45450      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45451      * 
45452      */
45453     cblack: false,
45454     /**
45455      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45456      * 
45457      */
45458     cwhite: false,
45459     
45460      /**
45461      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45462      * 
45463      */
45464     black: false,
45465     /**
45466      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45467      * 
45468      */
45469     white: false,
45470     
45471     // id of frame..
45472     frameId: false,
45473     
45474     // private properties
45475     validationEvent : false,
45476     deferHeight: true,
45477     initialized : false,
45478     activated : false,
45479     
45480     onFocus : Roo.emptyFn,
45481     iframePad:3,
45482     hideMode:'offsets',
45483     
45484     actionMode : 'container', // defaults to hiding it...
45485     
45486     defaultAutoCreate : { // modified by initCompnoent..
45487         tag: "textarea",
45488         style:"width:500px;height:300px;",
45489         autocomplete: "new-password"
45490     },
45491
45492     // private
45493     initComponent : function(){
45494         this.addEvents({
45495             /**
45496              * @event initialize
45497              * Fires when the editor is fully initialized (including the iframe)
45498              * @param {HtmlEditor} this
45499              */
45500             initialize: true,
45501             /**
45502              * @event activate
45503              * Fires when the editor is first receives the focus. Any insertion must wait
45504              * until after this event.
45505              * @param {HtmlEditor} this
45506              */
45507             activate: true,
45508              /**
45509              * @event beforesync
45510              * Fires before the textarea is updated with content from the editor iframe. Return false
45511              * to cancel the sync.
45512              * @param {HtmlEditor} this
45513              * @param {String} html
45514              */
45515             beforesync: true,
45516              /**
45517              * @event beforepush
45518              * Fires before the iframe editor is updated with content from the textarea. Return false
45519              * to cancel the push.
45520              * @param {HtmlEditor} this
45521              * @param {String} html
45522              */
45523             beforepush: true,
45524              /**
45525              * @event sync
45526              * Fires when the textarea is updated with content from the editor iframe.
45527              * @param {HtmlEditor} this
45528              * @param {String} html
45529              */
45530             sync: true,
45531              /**
45532              * @event push
45533              * Fires when the iframe editor is updated with content from the textarea.
45534              * @param {HtmlEditor} this
45535              * @param {String} html
45536              */
45537             push: true,
45538              /**
45539              * @event editmodechange
45540              * Fires when the editor switches edit modes
45541              * @param {HtmlEditor} this
45542              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45543              */
45544             editmodechange: true,
45545             /**
45546              * @event editorevent
45547              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45548              * @param {HtmlEditor} this
45549              */
45550             editorevent: true,
45551             /**
45552              * @event firstfocus
45553              * Fires when on first focus - needed by toolbars..
45554              * @param {HtmlEditor} this
45555              */
45556             firstfocus: true,
45557             /**
45558              * @event autosave
45559              * Auto save the htmlEditor value as a file into Events
45560              * @param {HtmlEditor} this
45561              */
45562             autosave: true,
45563             /**
45564              * @event savedpreview
45565              * preview the saved version of htmlEditor
45566              * @param {HtmlEditor} this
45567              */
45568             savedpreview: true,
45569             
45570             /**
45571             * @event stylesheetsclick
45572             * Fires when press the Sytlesheets button
45573             * @param {Roo.HtmlEditorCore} this
45574             */
45575             stylesheetsclick: true
45576         });
45577         this.defaultAutoCreate =  {
45578             tag: "textarea",
45579             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45580             autocomplete: "new-password"
45581         };
45582     },
45583
45584     /**
45585      * Protected method that will not generally be called directly. It
45586      * is called when the editor creates its toolbar. Override this method if you need to
45587      * add custom toolbar buttons.
45588      * @param {HtmlEditor} editor
45589      */
45590     createToolbar : function(editor){
45591         Roo.log("create toolbars");
45592         if (!editor.toolbars || !editor.toolbars.length) {
45593             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45594         }
45595         
45596         for (var i =0 ; i < editor.toolbars.length;i++) {
45597             editor.toolbars[i] = Roo.factory(
45598                     typeof(editor.toolbars[i]) == 'string' ?
45599                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45600                 Roo.form.HtmlEditor);
45601             editor.toolbars[i].init(editor);
45602         }
45603          
45604         
45605     },
45606
45607      
45608     // private
45609     onRender : function(ct, position)
45610     {
45611         var _t = this;
45612         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45613         
45614         this.wrap = this.el.wrap({
45615             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45616         });
45617         
45618         this.editorcore.onRender(ct, position);
45619          
45620         if (this.resizable) {
45621             this.resizeEl = new Roo.Resizable(this.wrap, {
45622                 pinned : true,
45623                 wrap: true,
45624                 dynamic : true,
45625                 minHeight : this.height,
45626                 height: this.height,
45627                 handles : this.resizable,
45628                 width: this.width,
45629                 listeners : {
45630                     resize : function(r, w, h) {
45631                         _t.onResize(w,h); // -something
45632                     }
45633                 }
45634             });
45635             
45636         }
45637         this.createToolbar(this);
45638        
45639         
45640         if(!this.width){
45641             this.setSize(this.wrap.getSize());
45642         }
45643         if (this.resizeEl) {
45644             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45645             // should trigger onReize..
45646         }
45647         
45648         this.keyNav = new Roo.KeyNav(this.el, {
45649             
45650             "tab" : function(e){
45651                 e.preventDefault();
45652                 
45653                 var value = this.getValue();
45654                 
45655                 var start = this.el.dom.selectionStart;
45656                 var end = this.el.dom.selectionEnd;
45657                 
45658                 if(!e.shiftKey){
45659                     
45660                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45661                     this.el.dom.setSelectionRange(end + 1, end + 1);
45662                     return;
45663                 }
45664                 
45665                 var f = value.substring(0, start).split("\t");
45666                 
45667                 if(f.pop().length != 0){
45668                     return;
45669                 }
45670                 
45671                 this.setValue(f.join("\t") + value.substring(end));
45672                 this.el.dom.setSelectionRange(start - 1, start - 1);
45673                 
45674             },
45675             
45676             "home" : function(e){
45677                 e.preventDefault();
45678                 
45679                 var curr = this.el.dom.selectionStart;
45680                 var lines = this.getValue().split("\n");
45681                 
45682                 if(!lines.length){
45683                     return;
45684                 }
45685                 
45686                 if(e.ctrlKey){
45687                     this.el.dom.setSelectionRange(0, 0);
45688                     return;
45689                 }
45690                 
45691                 var pos = 0;
45692                 
45693                 for (var i = 0; i < lines.length;i++) {
45694                     pos += lines[i].length;
45695                     
45696                     if(i != 0){
45697                         pos += 1;
45698                     }
45699                     
45700                     if(pos < curr){
45701                         continue;
45702                     }
45703                     
45704                     pos -= lines[i].length;
45705                     
45706                     break;
45707                 }
45708                 
45709                 if(!e.shiftKey){
45710                     this.el.dom.setSelectionRange(pos, pos);
45711                     return;
45712                 }
45713                 
45714                 this.el.dom.selectionStart = pos;
45715                 this.el.dom.selectionEnd = curr;
45716             },
45717             
45718             "end" : function(e){
45719                 e.preventDefault();
45720                 
45721                 var curr = this.el.dom.selectionStart;
45722                 var lines = this.getValue().split("\n");
45723                 
45724                 if(!lines.length){
45725                     return;
45726                 }
45727                 
45728                 if(e.ctrlKey){
45729                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45730                     return;
45731                 }
45732                 
45733                 var pos = 0;
45734                 
45735                 for (var i = 0; i < lines.length;i++) {
45736                     
45737                     pos += lines[i].length;
45738                     
45739                     if(i != 0){
45740                         pos += 1;
45741                     }
45742                     
45743                     if(pos < curr){
45744                         continue;
45745                     }
45746                     
45747                     break;
45748                 }
45749                 
45750                 if(!e.shiftKey){
45751                     this.el.dom.setSelectionRange(pos, pos);
45752                     return;
45753                 }
45754                 
45755                 this.el.dom.selectionStart = curr;
45756                 this.el.dom.selectionEnd = pos;
45757             },
45758
45759             scope : this,
45760
45761             doRelay : function(foo, bar, hname){
45762                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45763             },
45764
45765             forceKeyDown: true
45766         });
45767         
45768 //        if(this.autosave && this.w){
45769 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45770 //        }
45771     },
45772
45773     // private
45774     onResize : function(w, h)
45775     {
45776         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45777         var ew = false;
45778         var eh = false;
45779         
45780         if(this.el ){
45781             if(typeof w == 'number'){
45782                 var aw = w - this.wrap.getFrameWidth('lr');
45783                 this.el.setWidth(this.adjustWidth('textarea', aw));
45784                 ew = aw;
45785             }
45786             if(typeof h == 'number'){
45787                 var tbh = 0;
45788                 for (var i =0; i < this.toolbars.length;i++) {
45789                     // fixme - ask toolbars for heights?
45790                     tbh += this.toolbars[i].tb.el.getHeight();
45791                     if (this.toolbars[i].footer) {
45792                         tbh += this.toolbars[i].footer.el.getHeight();
45793                     }
45794                 }
45795                 
45796                 
45797                 
45798                 
45799                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45800                 ah -= 5; // knock a few pixes off for look..
45801 //                Roo.log(ah);
45802                 this.el.setHeight(this.adjustWidth('textarea', ah));
45803                 var eh = ah;
45804             }
45805         }
45806         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45807         this.editorcore.onResize(ew,eh);
45808         
45809     },
45810
45811     /**
45812      * Toggles the editor between standard and source edit mode.
45813      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45814      */
45815     toggleSourceEdit : function(sourceEditMode)
45816     {
45817         this.editorcore.toggleSourceEdit(sourceEditMode);
45818         
45819         if(this.editorcore.sourceEditMode){
45820             Roo.log('editor - showing textarea');
45821             
45822 //            Roo.log('in');
45823 //            Roo.log(this.syncValue());
45824             this.editorcore.syncValue();
45825             this.el.removeClass('x-hidden');
45826             this.el.dom.removeAttribute('tabIndex');
45827             this.el.focus();
45828             
45829             for (var i = 0; i < this.toolbars.length; i++) {
45830                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45831                     this.toolbars[i].tb.hide();
45832                     this.toolbars[i].footer.hide();
45833                 }
45834             }
45835             
45836         }else{
45837             Roo.log('editor - hiding textarea');
45838 //            Roo.log('out')
45839 //            Roo.log(this.pushValue()); 
45840             this.editorcore.pushValue();
45841             
45842             this.el.addClass('x-hidden');
45843             this.el.dom.setAttribute('tabIndex', -1);
45844             
45845             for (var i = 0; i < this.toolbars.length; i++) {
45846                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45847                     this.toolbars[i].tb.show();
45848                     this.toolbars[i].footer.show();
45849                 }
45850             }
45851             
45852             //this.deferFocus();
45853         }
45854         
45855         this.setSize(this.wrap.getSize());
45856         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45857         
45858         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45859     },
45860  
45861     // private (for BoxComponent)
45862     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45863
45864     // private (for BoxComponent)
45865     getResizeEl : function(){
45866         return this.wrap;
45867     },
45868
45869     // private (for BoxComponent)
45870     getPositionEl : function(){
45871         return this.wrap;
45872     },
45873
45874     // private
45875     initEvents : function(){
45876         this.originalValue = this.getValue();
45877     },
45878
45879     /**
45880      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45881      * @method
45882      */
45883     markInvalid : Roo.emptyFn,
45884     /**
45885      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45886      * @method
45887      */
45888     clearInvalid : Roo.emptyFn,
45889
45890     setValue : function(v){
45891         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45892         this.editorcore.pushValue();
45893     },
45894
45895      
45896     // private
45897     deferFocus : function(){
45898         this.focus.defer(10, this);
45899     },
45900
45901     // doc'ed in Field
45902     focus : function(){
45903         this.editorcore.focus();
45904         
45905     },
45906       
45907
45908     // private
45909     onDestroy : function(){
45910         
45911         
45912         
45913         if(this.rendered){
45914             
45915             for (var i =0; i < this.toolbars.length;i++) {
45916                 // fixme - ask toolbars for heights?
45917                 this.toolbars[i].onDestroy();
45918             }
45919             
45920             this.wrap.dom.innerHTML = '';
45921             this.wrap.remove();
45922         }
45923     },
45924
45925     // private
45926     onFirstFocus : function(){
45927         //Roo.log("onFirstFocus");
45928         this.editorcore.onFirstFocus();
45929          for (var i =0; i < this.toolbars.length;i++) {
45930             this.toolbars[i].onFirstFocus();
45931         }
45932         
45933     },
45934     
45935     // private
45936     syncValue : function()
45937     {
45938         this.editorcore.syncValue();
45939     },
45940     
45941     pushValue : function()
45942     {
45943         this.editorcore.pushValue();
45944     },
45945     
45946     setStylesheets : function(stylesheets)
45947     {
45948         this.editorcore.setStylesheets(stylesheets);
45949     },
45950     
45951     removeStylesheets : function()
45952     {
45953         this.editorcore.removeStylesheets();
45954     }
45955      
45956     
45957     // hide stuff that is not compatible
45958     /**
45959      * @event blur
45960      * @hide
45961      */
45962     /**
45963      * @event change
45964      * @hide
45965      */
45966     /**
45967      * @event focus
45968      * @hide
45969      */
45970     /**
45971      * @event specialkey
45972      * @hide
45973      */
45974     /**
45975      * @cfg {String} fieldClass @hide
45976      */
45977     /**
45978      * @cfg {String} focusClass @hide
45979      */
45980     /**
45981      * @cfg {String} autoCreate @hide
45982      */
45983     /**
45984      * @cfg {String} inputType @hide
45985      */
45986     /**
45987      * @cfg {String} invalidClass @hide
45988      */
45989     /**
45990      * @cfg {String} invalidText @hide
45991      */
45992     /**
45993      * @cfg {String} msgFx @hide
45994      */
45995     /**
45996      * @cfg {String} validateOnBlur @hide
45997      */
45998 });
45999  
46000     // <script type="text/javascript">
46001 /*
46002  * Based on
46003  * Ext JS Library 1.1.1
46004  * Copyright(c) 2006-2007, Ext JS, LLC.
46005  *  
46006  
46007  */
46008
46009 /**
46010  * @class Roo.form.HtmlEditorToolbar1
46011  * Basic Toolbar
46012  * 
46013  * Usage:
46014  *
46015  new Roo.form.HtmlEditor({
46016     ....
46017     toolbars : [
46018         new Roo.form.HtmlEditorToolbar1({
46019             disable : { fonts: 1 , format: 1, ..., ... , ...],
46020             btns : [ .... ]
46021         })
46022     }
46023      
46024  * 
46025  * @cfg {Object} disable List of elements to disable..
46026  * @cfg {Array} btns List of additional buttons.
46027  * 
46028  * 
46029  * NEEDS Extra CSS? 
46030  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46031  */
46032  
46033 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46034 {
46035     
46036     Roo.apply(this, config);
46037     
46038     // default disabled, based on 'good practice'..
46039     this.disable = this.disable || {};
46040     Roo.applyIf(this.disable, {
46041         fontSize : true,
46042         colors : true,
46043         specialElements : true
46044     });
46045     
46046     
46047     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46048     // dont call parent... till later.
46049 }
46050
46051 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46052     
46053     tb: false,
46054     
46055     rendered: false,
46056     
46057     editor : false,
46058     editorcore : false,
46059     /**
46060      * @cfg {Object} disable  List of toolbar elements to disable
46061          
46062      */
46063     disable : false,
46064     
46065     
46066      /**
46067      * @cfg {String} createLinkText The default text for the create link prompt
46068      */
46069     createLinkText : 'Please enter the URL for the link:',
46070     /**
46071      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46072      */
46073     defaultLinkValue : 'http:/'+'/',
46074    
46075     
46076       /**
46077      * @cfg {Array} fontFamilies An array of available font families
46078      */
46079     fontFamilies : [
46080         'Arial',
46081         'Courier New',
46082         'Tahoma',
46083         'Times New Roman',
46084         'Verdana'
46085     ],
46086     
46087     specialChars : [
46088            "&#169;",
46089           "&#174;",     
46090           "&#8482;",    
46091           "&#163;" ,    
46092          // "&#8212;",    
46093           "&#8230;",    
46094           "&#247;" ,    
46095         //  "&#225;" ,     ?? a acute?
46096            "&#8364;"    , //Euro
46097        //   "&#8220;"    ,
46098         //  "&#8221;"    ,
46099         //  "&#8226;"    ,
46100           "&#176;"  //   , // degrees
46101
46102          // "&#233;"     , // e ecute
46103          // "&#250;"     , // u ecute?
46104     ],
46105     
46106     specialElements : [
46107         {
46108             text: "Insert Table",
46109             xtype: 'MenuItem',
46110             xns : Roo.Menu,
46111             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46112                 
46113         },
46114         {    
46115             text: "Insert Image",
46116             xtype: 'MenuItem',
46117             xns : Roo.Menu,
46118             ihtml : '<img src="about:blank"/>'
46119             
46120         }
46121         
46122          
46123     ],
46124     
46125     
46126     inputElements : [ 
46127             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46128             "input:submit", "input:button", "select", "textarea", "label" ],
46129     formats : [
46130         ["p"] ,  
46131         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46132         ["pre"],[ "code"], 
46133         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46134         ['div'],['span'],
46135         ['sup'],['sub']
46136     ],
46137     
46138     cleanStyles : [
46139         "font-size"
46140     ],
46141      /**
46142      * @cfg {String} defaultFont default font to use.
46143      */
46144     defaultFont: 'tahoma',
46145    
46146     fontSelect : false,
46147     
46148     
46149     formatCombo : false,
46150     
46151     init : function(editor)
46152     {
46153         this.editor = editor;
46154         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46155         var editorcore = this.editorcore;
46156         
46157         var _t = this;
46158         
46159         var fid = editorcore.frameId;
46160         var etb = this;
46161         function btn(id, toggle, handler){
46162             var xid = fid + '-'+ id ;
46163             return {
46164                 id : xid,
46165                 cmd : id,
46166                 cls : 'x-btn-icon x-edit-'+id,
46167                 enableToggle:toggle !== false,
46168                 scope: _t, // was editor...
46169                 handler:handler||_t.relayBtnCmd,
46170                 clickEvent:'mousedown',
46171                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46172                 tabIndex:-1
46173             };
46174         }
46175         
46176         
46177         
46178         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46179         this.tb = tb;
46180          // stop form submits
46181         tb.el.on('click', function(e){
46182             e.preventDefault(); // what does this do?
46183         });
46184
46185         if(!this.disable.font) { // && !Roo.isSafari){
46186             /* why no safari for fonts 
46187             editor.fontSelect = tb.el.createChild({
46188                 tag:'select',
46189                 tabIndex: -1,
46190                 cls:'x-font-select',
46191                 html: this.createFontOptions()
46192             });
46193             
46194             editor.fontSelect.on('change', function(){
46195                 var font = editor.fontSelect.dom.value;
46196                 editor.relayCmd('fontname', font);
46197                 editor.deferFocus();
46198             }, editor);
46199             
46200             tb.add(
46201                 editor.fontSelect.dom,
46202                 '-'
46203             );
46204             */
46205             
46206         };
46207         if(!this.disable.formats){
46208             this.formatCombo = new Roo.form.ComboBox({
46209                 store: new Roo.data.SimpleStore({
46210                     id : 'tag',
46211                     fields: ['tag'],
46212                     data : this.formats // from states.js
46213                 }),
46214                 blockFocus : true,
46215                 name : '',
46216                 //autoCreate : {tag: "div",  size: "20"},
46217                 displayField:'tag',
46218                 typeAhead: false,
46219                 mode: 'local',
46220                 editable : false,
46221                 triggerAction: 'all',
46222                 emptyText:'Add tag',
46223                 selectOnFocus:true,
46224                 width:135,
46225                 listeners : {
46226                     'select': function(c, r, i) {
46227                         editorcore.insertTag(r.get('tag'));
46228                         editor.focus();
46229                     }
46230                 }
46231
46232             });
46233             tb.addField(this.formatCombo);
46234             
46235         }
46236         
46237         if(!this.disable.format){
46238             tb.add(
46239                 btn('bold'),
46240                 btn('italic'),
46241                 btn('underline'),
46242                 btn('strikethrough')
46243             );
46244         };
46245         if(!this.disable.fontSize){
46246             tb.add(
46247                 '-',
46248                 
46249                 
46250                 btn('increasefontsize', false, editorcore.adjustFont),
46251                 btn('decreasefontsize', false, editorcore.adjustFont)
46252             );
46253         };
46254         
46255         
46256         if(!this.disable.colors){
46257             tb.add(
46258                 '-', {
46259                     id:editorcore.frameId +'-forecolor',
46260                     cls:'x-btn-icon x-edit-forecolor',
46261                     clickEvent:'mousedown',
46262                     tooltip: this.buttonTips['forecolor'] || undefined,
46263                     tabIndex:-1,
46264                     menu : new Roo.menu.ColorMenu({
46265                         allowReselect: true,
46266                         focus: Roo.emptyFn,
46267                         value:'000000',
46268                         plain:true,
46269                         selectHandler: function(cp, color){
46270                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46271                             editor.deferFocus();
46272                         },
46273                         scope: editorcore,
46274                         clickEvent:'mousedown'
46275                     })
46276                 }, {
46277                     id:editorcore.frameId +'backcolor',
46278                     cls:'x-btn-icon x-edit-backcolor',
46279                     clickEvent:'mousedown',
46280                     tooltip: this.buttonTips['backcolor'] || undefined,
46281                     tabIndex:-1,
46282                     menu : new Roo.menu.ColorMenu({
46283                         focus: Roo.emptyFn,
46284                         value:'FFFFFF',
46285                         plain:true,
46286                         allowReselect: true,
46287                         selectHandler: function(cp, color){
46288                             if(Roo.isGecko){
46289                                 editorcore.execCmd('useCSS', false);
46290                                 editorcore.execCmd('hilitecolor', color);
46291                                 editorcore.execCmd('useCSS', true);
46292                                 editor.deferFocus();
46293                             }else{
46294                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46295                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46296                                 editor.deferFocus();
46297                             }
46298                         },
46299                         scope:editorcore,
46300                         clickEvent:'mousedown'
46301                     })
46302                 }
46303             );
46304         };
46305         // now add all the items...
46306         
46307
46308         if(!this.disable.alignments){
46309             tb.add(
46310                 '-',
46311                 btn('justifyleft'),
46312                 btn('justifycenter'),
46313                 btn('justifyright')
46314             );
46315         };
46316
46317         //if(!Roo.isSafari){
46318             if(!this.disable.links){
46319                 tb.add(
46320                     '-',
46321                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46322                 );
46323             };
46324
46325             if(!this.disable.lists){
46326                 tb.add(
46327                     '-',
46328                     btn('insertorderedlist'),
46329                     btn('insertunorderedlist')
46330                 );
46331             }
46332             if(!this.disable.sourceEdit){
46333                 tb.add(
46334                     '-',
46335                     btn('sourceedit', true, function(btn){
46336                         this.toggleSourceEdit(btn.pressed);
46337                     })
46338                 );
46339             }
46340         //}
46341         
46342         var smenu = { };
46343         // special menu.. - needs to be tidied up..
46344         if (!this.disable.special) {
46345             smenu = {
46346                 text: "&#169;",
46347                 cls: 'x-edit-none',
46348                 
46349                 menu : {
46350                     items : []
46351                 }
46352             };
46353             for (var i =0; i < this.specialChars.length; i++) {
46354                 smenu.menu.items.push({
46355                     
46356                     html: this.specialChars[i],
46357                     handler: function(a,b) {
46358                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46359                         //editor.insertAtCursor(a.html);
46360                         
46361                     },
46362                     tabIndex:-1
46363                 });
46364             }
46365             
46366             
46367             tb.add(smenu);
46368             
46369             
46370         }
46371         
46372         var cmenu = { };
46373         if (!this.disable.cleanStyles) {
46374             cmenu = {
46375                 cls: 'x-btn-icon x-btn-clear',
46376                 
46377                 menu : {
46378                     items : []
46379                 }
46380             };
46381             for (var i =0; i < this.cleanStyles.length; i++) {
46382                 cmenu.menu.items.push({
46383                     actiontype : this.cleanStyles[i],
46384                     html: 'Remove ' + this.cleanStyles[i],
46385                     handler: function(a,b) {
46386 //                        Roo.log(a);
46387 //                        Roo.log(b);
46388                         var c = Roo.get(editorcore.doc.body);
46389                         c.select('[style]').each(function(s) {
46390                             s.dom.style.removeProperty(a.actiontype);
46391                         });
46392                         editorcore.syncValue();
46393                     },
46394                     tabIndex:-1
46395                 });
46396             }
46397              cmenu.menu.items.push({
46398                 actiontype : 'tablewidths',
46399                 html: 'Remove Table Widths',
46400                 handler: function(a,b) {
46401                     editorcore.cleanTableWidths();
46402                     editorcore.syncValue();
46403                 },
46404                 tabIndex:-1
46405             });
46406             cmenu.menu.items.push({
46407                 actiontype : 'word',
46408                 html: 'Remove MS Word Formating',
46409                 handler: function(a,b) {
46410                     editorcore.cleanWord();
46411                     editorcore.syncValue();
46412                 },
46413                 tabIndex:-1
46414             });
46415             
46416             cmenu.menu.items.push({
46417                 actiontype : 'all',
46418                 html: 'Remove All Styles',
46419                 handler: function(a,b) {
46420                     
46421                     var c = Roo.get(editorcore.doc.body);
46422                     c.select('[style]').each(function(s) {
46423                         s.dom.removeAttribute('style');
46424                     });
46425                     editorcore.syncValue();
46426                 },
46427                 tabIndex:-1
46428             });
46429             
46430             cmenu.menu.items.push({
46431                 actiontype : 'all',
46432                 html: 'Remove All CSS Classes',
46433                 handler: function(a,b) {
46434                     
46435                     var c = Roo.get(editorcore.doc.body);
46436                     c.select('[class]').each(function(s) {
46437                         s.dom.removeAttribute('class');
46438                     });
46439                     editorcore.cleanWord();
46440                     editorcore.syncValue();
46441                 },
46442                 tabIndex:-1
46443             });
46444             
46445              cmenu.menu.items.push({
46446                 actiontype : 'tidy',
46447                 html: 'Tidy HTML Source',
46448                 handler: function(a,b) {
46449                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46450                     editorcore.syncValue();
46451                 },
46452                 tabIndex:-1
46453             });
46454             
46455             
46456             tb.add(cmenu);
46457         }
46458          
46459         if (!this.disable.specialElements) {
46460             var semenu = {
46461                 text: "Other;",
46462                 cls: 'x-edit-none',
46463                 menu : {
46464                     items : []
46465                 }
46466             };
46467             for (var i =0; i < this.specialElements.length; i++) {
46468                 semenu.menu.items.push(
46469                     Roo.apply({ 
46470                         handler: function(a,b) {
46471                             editor.insertAtCursor(this.ihtml);
46472                         }
46473                     }, this.specialElements[i])
46474                 );
46475                     
46476             }
46477             
46478             tb.add(semenu);
46479             
46480             
46481         }
46482          
46483         
46484         if (this.btns) {
46485             for(var i =0; i< this.btns.length;i++) {
46486                 var b = Roo.factory(this.btns[i],Roo.form);
46487                 b.cls =  'x-edit-none';
46488                 
46489                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46490                     b.cls += ' x-init-enable';
46491                 }
46492                 
46493                 b.scope = editorcore;
46494                 tb.add(b);
46495             }
46496         
46497         }
46498         
46499         
46500         
46501         // disable everything...
46502         
46503         this.tb.items.each(function(item){
46504             
46505            if(
46506                 item.id != editorcore.frameId+ '-sourceedit' && 
46507                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46508             ){
46509                 
46510                 item.disable();
46511             }
46512         });
46513         this.rendered = true;
46514         
46515         // the all the btns;
46516         editor.on('editorevent', this.updateToolbar, this);
46517         // other toolbars need to implement this..
46518         //editor.on('editmodechange', this.updateToolbar, this);
46519     },
46520     
46521     
46522     relayBtnCmd : function(btn) {
46523         this.editorcore.relayCmd(btn.cmd);
46524     },
46525     // private used internally
46526     createLink : function(){
46527         Roo.log("create link?");
46528         var url = prompt(this.createLinkText, this.defaultLinkValue);
46529         if(url && url != 'http:/'+'/'){
46530             this.editorcore.relayCmd('createlink', url);
46531         }
46532     },
46533
46534     
46535     /**
46536      * Protected method that will not generally be called directly. It triggers
46537      * a toolbar update by reading the markup state of the current selection in the editor.
46538      */
46539     updateToolbar: function(){
46540
46541         if(!this.editorcore.activated){
46542             this.editor.onFirstFocus();
46543             return;
46544         }
46545
46546         var btns = this.tb.items.map, 
46547             doc = this.editorcore.doc,
46548             frameId = this.editorcore.frameId;
46549
46550         if(!this.disable.font && !Roo.isSafari){
46551             /*
46552             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46553             if(name != this.fontSelect.dom.value){
46554                 this.fontSelect.dom.value = name;
46555             }
46556             */
46557         }
46558         if(!this.disable.format){
46559             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46560             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46561             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46562             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46563         }
46564         if(!this.disable.alignments){
46565             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46566             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46567             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46568         }
46569         if(!Roo.isSafari && !this.disable.lists){
46570             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46571             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46572         }
46573         
46574         var ans = this.editorcore.getAllAncestors();
46575         if (this.formatCombo) {
46576             
46577             
46578             var store = this.formatCombo.store;
46579             this.formatCombo.setValue("");
46580             for (var i =0; i < ans.length;i++) {
46581                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46582                     // select it..
46583                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46584                     break;
46585                 }
46586             }
46587         }
46588         
46589         
46590         
46591         // hides menus... - so this cant be on a menu...
46592         Roo.menu.MenuMgr.hideAll();
46593
46594         //this.editorsyncValue();
46595     },
46596    
46597     
46598     createFontOptions : function(){
46599         var buf = [], fs = this.fontFamilies, ff, lc;
46600         
46601         
46602         
46603         for(var i = 0, len = fs.length; i< len; i++){
46604             ff = fs[i];
46605             lc = ff.toLowerCase();
46606             buf.push(
46607                 '<option value="',lc,'" style="font-family:',ff,';"',
46608                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46609                     ff,
46610                 '</option>'
46611             );
46612         }
46613         return buf.join('');
46614     },
46615     
46616     toggleSourceEdit : function(sourceEditMode){
46617         
46618         Roo.log("toolbar toogle");
46619         if(sourceEditMode === undefined){
46620             sourceEditMode = !this.sourceEditMode;
46621         }
46622         this.sourceEditMode = sourceEditMode === true;
46623         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46624         // just toggle the button?
46625         if(btn.pressed !== this.sourceEditMode){
46626             btn.toggle(this.sourceEditMode);
46627             return;
46628         }
46629         
46630         if(sourceEditMode){
46631             Roo.log("disabling buttons");
46632             this.tb.items.each(function(item){
46633                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46634                     item.disable();
46635                 }
46636             });
46637           
46638         }else{
46639             Roo.log("enabling buttons");
46640             if(this.editorcore.initialized){
46641                 this.tb.items.each(function(item){
46642                     item.enable();
46643                 });
46644             }
46645             
46646         }
46647         Roo.log("calling toggole on editor");
46648         // tell the editor that it's been pressed..
46649         this.editor.toggleSourceEdit(sourceEditMode);
46650        
46651     },
46652      /**
46653      * Object collection of toolbar tooltips for the buttons in the editor. The key
46654      * is the command id associated with that button and the value is a valid QuickTips object.
46655      * For example:
46656 <pre><code>
46657 {
46658     bold : {
46659         title: 'Bold (Ctrl+B)',
46660         text: 'Make the selected text bold.',
46661         cls: 'x-html-editor-tip'
46662     },
46663     italic : {
46664         title: 'Italic (Ctrl+I)',
46665         text: 'Make the selected text italic.',
46666         cls: 'x-html-editor-tip'
46667     },
46668     ...
46669 </code></pre>
46670     * @type Object
46671      */
46672     buttonTips : {
46673         bold : {
46674             title: 'Bold (Ctrl+B)',
46675             text: 'Make the selected text bold.',
46676             cls: 'x-html-editor-tip'
46677         },
46678         italic : {
46679             title: 'Italic (Ctrl+I)',
46680             text: 'Make the selected text italic.',
46681             cls: 'x-html-editor-tip'
46682         },
46683         underline : {
46684             title: 'Underline (Ctrl+U)',
46685             text: 'Underline the selected text.',
46686             cls: 'x-html-editor-tip'
46687         },
46688         strikethrough : {
46689             title: 'Strikethrough',
46690             text: 'Strikethrough the selected text.',
46691             cls: 'x-html-editor-tip'
46692         },
46693         increasefontsize : {
46694             title: 'Grow Text',
46695             text: 'Increase the font size.',
46696             cls: 'x-html-editor-tip'
46697         },
46698         decreasefontsize : {
46699             title: 'Shrink Text',
46700             text: 'Decrease the font size.',
46701             cls: 'x-html-editor-tip'
46702         },
46703         backcolor : {
46704             title: 'Text Highlight Color',
46705             text: 'Change the background color of the selected text.',
46706             cls: 'x-html-editor-tip'
46707         },
46708         forecolor : {
46709             title: 'Font Color',
46710             text: 'Change the color of the selected text.',
46711             cls: 'x-html-editor-tip'
46712         },
46713         justifyleft : {
46714             title: 'Align Text Left',
46715             text: 'Align text to the left.',
46716             cls: 'x-html-editor-tip'
46717         },
46718         justifycenter : {
46719             title: 'Center Text',
46720             text: 'Center text in the editor.',
46721             cls: 'x-html-editor-tip'
46722         },
46723         justifyright : {
46724             title: 'Align Text Right',
46725             text: 'Align text to the right.',
46726             cls: 'x-html-editor-tip'
46727         },
46728         insertunorderedlist : {
46729             title: 'Bullet List',
46730             text: 'Start a bulleted list.',
46731             cls: 'x-html-editor-tip'
46732         },
46733         insertorderedlist : {
46734             title: 'Numbered List',
46735             text: 'Start a numbered list.',
46736             cls: 'x-html-editor-tip'
46737         },
46738         createlink : {
46739             title: 'Hyperlink',
46740             text: 'Make the selected text a hyperlink.',
46741             cls: 'x-html-editor-tip'
46742         },
46743         sourceedit : {
46744             title: 'Source Edit',
46745             text: 'Switch to source editing mode.',
46746             cls: 'x-html-editor-tip'
46747         }
46748     },
46749     // private
46750     onDestroy : function(){
46751         if(this.rendered){
46752             
46753             this.tb.items.each(function(item){
46754                 if(item.menu){
46755                     item.menu.removeAll();
46756                     if(item.menu.el){
46757                         item.menu.el.destroy();
46758                     }
46759                 }
46760                 item.destroy();
46761             });
46762              
46763         }
46764     },
46765     onFirstFocus: function() {
46766         this.tb.items.each(function(item){
46767            item.enable();
46768         });
46769     }
46770 });
46771
46772
46773
46774
46775 // <script type="text/javascript">
46776 /*
46777  * Based on
46778  * Ext JS Library 1.1.1
46779  * Copyright(c) 2006-2007, Ext JS, LLC.
46780  *  
46781  
46782  */
46783
46784  
46785 /**
46786  * @class Roo.form.HtmlEditor.ToolbarContext
46787  * Context Toolbar
46788  * 
46789  * Usage:
46790  *
46791  new Roo.form.HtmlEditor({
46792     ....
46793     toolbars : [
46794         { xtype: 'ToolbarStandard', styles : {} }
46795         { xtype: 'ToolbarContext', disable : {} }
46796     ]
46797 })
46798
46799      
46800  * 
46801  * @config : {Object} disable List of elements to disable.. (not done yet.)
46802  * @config : {Object} styles  Map of styles available.
46803  * 
46804  */
46805
46806 Roo.form.HtmlEditor.ToolbarContext = function(config)
46807 {
46808     
46809     Roo.apply(this, config);
46810     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46811     // dont call parent... till later.
46812     this.styles = this.styles || {};
46813 }
46814
46815  
46816
46817 Roo.form.HtmlEditor.ToolbarContext.types = {
46818     'IMG' : {
46819         width : {
46820             title: "Width",
46821             width: 40
46822         },
46823         height:  {
46824             title: "Height",
46825             width: 40
46826         },
46827         align: {
46828             title: "Align",
46829             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46830             width : 80
46831             
46832         },
46833         border: {
46834             title: "Border",
46835             width: 40
46836         },
46837         alt: {
46838             title: "Alt",
46839             width: 120
46840         },
46841         src : {
46842             title: "Src",
46843             width: 220
46844         }
46845         
46846     },
46847     'A' : {
46848         name : {
46849             title: "Name",
46850             width: 50
46851         },
46852         target:  {
46853             title: "Target",
46854             width: 120
46855         },
46856         href:  {
46857             title: "Href",
46858             width: 220
46859         } // border?
46860         
46861     },
46862     'TABLE' : {
46863         rows : {
46864             title: "Rows",
46865             width: 20
46866         },
46867         cols : {
46868             title: "Cols",
46869             width: 20
46870         },
46871         width : {
46872             title: "Width",
46873             width: 40
46874         },
46875         height : {
46876             title: "Height",
46877             width: 40
46878         },
46879         border : {
46880             title: "Border",
46881             width: 20
46882         }
46883     },
46884     'TD' : {
46885         width : {
46886             title: "Width",
46887             width: 40
46888         },
46889         height : {
46890             title: "Height",
46891             width: 40
46892         },   
46893         align: {
46894             title: "Align",
46895             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46896             width: 80
46897         },
46898         valign: {
46899             title: "Valign",
46900             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46901             width: 80
46902         },
46903         colspan: {
46904             title: "Colspan",
46905             width: 20
46906             
46907         },
46908          'font-family'  : {
46909             title : "Font",
46910             style : 'fontFamily',
46911             displayField: 'display',
46912             optname : 'font-family',
46913             width: 140
46914         }
46915     },
46916     'INPUT' : {
46917         name : {
46918             title: "name",
46919             width: 120
46920         },
46921         value : {
46922             title: "Value",
46923             width: 120
46924         },
46925         width : {
46926             title: "Width",
46927             width: 40
46928         }
46929     },
46930     'LABEL' : {
46931         'for' : {
46932             title: "For",
46933             width: 120
46934         }
46935     },
46936     'TEXTAREA' : {
46937           name : {
46938             title: "name",
46939             width: 120
46940         },
46941         rows : {
46942             title: "Rows",
46943             width: 20
46944         },
46945         cols : {
46946             title: "Cols",
46947             width: 20
46948         }
46949     },
46950     'SELECT' : {
46951         name : {
46952             title: "name",
46953             width: 120
46954         },
46955         selectoptions : {
46956             title: "Options",
46957             width: 200
46958         }
46959     },
46960     
46961     // should we really allow this??
46962     // should this just be 
46963     'BODY' : {
46964         title : {
46965             title: "Title",
46966             width: 200,
46967             disabled : true
46968         }
46969     },
46970     'SPAN' : {
46971         'font-family'  : {
46972             title : "Font",
46973             style : 'fontFamily',
46974             displayField: 'display',
46975             optname : 'font-family',
46976             width: 140
46977         }
46978     },
46979     'DIV' : {
46980         'font-family'  : {
46981             title : "Font",
46982             style : 'fontFamily',
46983             displayField: 'display',
46984             optname : 'font-family',
46985             width: 140
46986         }
46987     },
46988      'P' : {
46989         'font-family'  : {
46990             title : "Font",
46991             style : 'fontFamily',
46992             displayField: 'display',
46993             optname : 'font-family',
46994             width: 140
46995         }
46996     },
46997     
46998     '*' : {
46999         // empty..
47000     }
47001
47002 };
47003
47004 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47005 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47006
47007 Roo.form.HtmlEditor.ToolbarContext.options = {
47008         'font-family'  : [ 
47009                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47010                 [ 'Courier New', 'Courier New'],
47011                 [ 'Tahoma', 'Tahoma'],
47012                 [ 'Times New Roman,serif', 'Times'],
47013                 [ 'Verdana','Verdana' ]
47014         ]
47015 };
47016
47017 // fixme - these need to be configurable..
47018  
47019
47020 //Roo.form.HtmlEditor.ToolbarContext.types
47021
47022
47023 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47024     
47025     tb: false,
47026     
47027     rendered: false,
47028     
47029     editor : false,
47030     editorcore : false,
47031     /**
47032      * @cfg {Object} disable  List of toolbar elements to disable
47033          
47034      */
47035     disable : false,
47036     /**
47037      * @cfg {Object} styles List of styles 
47038      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47039      *
47040      * These must be defined in the page, so they get rendered correctly..
47041      * .headline { }
47042      * TD.underline { }
47043      * 
47044      */
47045     styles : false,
47046     
47047     options: false,
47048     
47049     toolbars : false,
47050     
47051     init : function(editor)
47052     {
47053         this.editor = editor;
47054         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47055         var editorcore = this.editorcore;
47056         
47057         var fid = editorcore.frameId;
47058         var etb = this;
47059         function btn(id, toggle, handler){
47060             var xid = fid + '-'+ id ;
47061             return {
47062                 id : xid,
47063                 cmd : id,
47064                 cls : 'x-btn-icon x-edit-'+id,
47065                 enableToggle:toggle !== false,
47066                 scope: editorcore, // was editor...
47067                 handler:handler||editorcore.relayBtnCmd,
47068                 clickEvent:'mousedown',
47069                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47070                 tabIndex:-1
47071             };
47072         }
47073         // create a new element.
47074         var wdiv = editor.wrap.createChild({
47075                 tag: 'div'
47076             }, editor.wrap.dom.firstChild.nextSibling, true);
47077         
47078         // can we do this more than once??
47079         
47080          // stop form submits
47081       
47082  
47083         // disable everything...
47084         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47085         this.toolbars = {};
47086            
47087         for (var i in  ty) {
47088           
47089             this.toolbars[i] = this.buildToolbar(ty[i],i);
47090         }
47091         this.tb = this.toolbars.BODY;
47092         this.tb.el.show();
47093         this.buildFooter();
47094         this.footer.show();
47095         editor.on('hide', function( ) { this.footer.hide() }, this);
47096         editor.on('show', function( ) { this.footer.show() }, this);
47097         
47098          
47099         this.rendered = true;
47100         
47101         // the all the btns;
47102         editor.on('editorevent', this.updateToolbar, this);
47103         // other toolbars need to implement this..
47104         //editor.on('editmodechange', this.updateToolbar, this);
47105     },
47106     
47107     
47108     
47109     /**
47110      * Protected method that will not generally be called directly. It triggers
47111      * a toolbar update by reading the markup state of the current selection in the editor.
47112      *
47113      * Note you can force an update by calling on('editorevent', scope, false)
47114      */
47115     updateToolbar: function(editor,ev,sel){
47116
47117         //Roo.log(ev);
47118         // capture mouse up - this is handy for selecting images..
47119         // perhaps should go somewhere else...
47120         if(!this.editorcore.activated){
47121              this.editor.onFirstFocus();
47122             return;
47123         }
47124         
47125         
47126         
47127         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47128         // selectNode - might want to handle IE?
47129         if (ev &&
47130             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47131             ev.target && ev.target.tagName == 'IMG') {
47132             // they have click on an image...
47133             // let's see if we can change the selection...
47134             sel = ev.target;
47135          
47136               var nodeRange = sel.ownerDocument.createRange();
47137             try {
47138                 nodeRange.selectNode(sel);
47139             } catch (e) {
47140                 nodeRange.selectNodeContents(sel);
47141             }
47142             //nodeRange.collapse(true);
47143             var s = this.editorcore.win.getSelection();
47144             s.removeAllRanges();
47145             s.addRange(nodeRange);
47146         }  
47147         
47148       
47149         var updateFooter = sel ? false : true;
47150         
47151         
47152         var ans = this.editorcore.getAllAncestors();
47153         
47154         // pick
47155         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47156         
47157         if (!sel) { 
47158             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47159             sel = sel ? sel : this.editorcore.doc.body;
47160             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47161             
47162         }
47163         // pick a menu that exists..
47164         var tn = sel.tagName.toUpperCase();
47165         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47166         
47167         tn = sel.tagName.toUpperCase();
47168         
47169         var lastSel = this.tb.selectedNode;
47170         
47171         this.tb.selectedNode = sel;
47172         
47173         // if current menu does not match..
47174         
47175         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47176                 
47177             this.tb.el.hide();
47178             ///console.log("show: " + tn);
47179             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47180             this.tb.el.show();
47181             // update name
47182             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47183             
47184             
47185             // update attributes
47186             if (this.tb.fields) {
47187                 this.tb.fields.each(function(e) {
47188                     if (e.stylename) {
47189                         e.setValue(sel.style[e.stylename]);
47190                         return;
47191                     } 
47192                    e.setValue(sel.getAttribute(e.attrname));
47193                 });
47194             }
47195             
47196             var hasStyles = false;
47197             for(var i in this.styles) {
47198                 hasStyles = true;
47199                 break;
47200             }
47201             
47202             // update styles
47203             if (hasStyles) { 
47204                 var st = this.tb.fields.item(0);
47205                 
47206                 st.store.removeAll();
47207                
47208                 
47209                 var cn = sel.className.split(/\s+/);
47210                 
47211                 var avs = [];
47212                 if (this.styles['*']) {
47213                     
47214                     Roo.each(this.styles['*'], function(v) {
47215                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47216                     });
47217                 }
47218                 if (this.styles[tn]) { 
47219                     Roo.each(this.styles[tn], function(v) {
47220                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47221                     });
47222                 }
47223                 
47224                 st.store.loadData(avs);
47225                 st.collapse();
47226                 st.setValue(cn);
47227             }
47228             // flag our selected Node.
47229             this.tb.selectedNode = sel;
47230            
47231            
47232             Roo.menu.MenuMgr.hideAll();
47233
47234         }
47235         
47236         if (!updateFooter) {
47237             //this.footDisp.dom.innerHTML = ''; 
47238             return;
47239         }
47240         // update the footer
47241         //
47242         var html = '';
47243         
47244         this.footerEls = ans.reverse();
47245         Roo.each(this.footerEls, function(a,i) {
47246             if (!a) { return; }
47247             html += html.length ? ' &gt; '  :  '';
47248             
47249             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47250             
47251         });
47252        
47253         // 
47254         var sz = this.footDisp.up('td').getSize();
47255         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47256         this.footDisp.dom.style.marginLeft = '5px';
47257         
47258         this.footDisp.dom.style.overflow = 'hidden';
47259         
47260         this.footDisp.dom.innerHTML = html;
47261             
47262         //this.editorsyncValue();
47263     },
47264      
47265     
47266    
47267        
47268     // private
47269     onDestroy : function(){
47270         if(this.rendered){
47271             
47272             this.tb.items.each(function(item){
47273                 if(item.menu){
47274                     item.menu.removeAll();
47275                     if(item.menu.el){
47276                         item.menu.el.destroy();
47277                     }
47278                 }
47279                 item.destroy();
47280             });
47281              
47282         }
47283     },
47284     onFirstFocus: function() {
47285         // need to do this for all the toolbars..
47286         this.tb.items.each(function(item){
47287            item.enable();
47288         });
47289     },
47290     buildToolbar: function(tlist, nm)
47291     {
47292         var editor = this.editor;
47293         var editorcore = this.editorcore;
47294          // create a new element.
47295         var wdiv = editor.wrap.createChild({
47296                 tag: 'div'
47297             }, editor.wrap.dom.firstChild.nextSibling, true);
47298         
47299        
47300         var tb = new Roo.Toolbar(wdiv);
47301         // add the name..
47302         
47303         tb.add(nm+ ":&nbsp;");
47304         
47305         var styles = [];
47306         for(var i in this.styles) {
47307             styles.push(i);
47308         }
47309         
47310         // styles...
47311         if (styles && styles.length) {
47312             
47313             // this needs a multi-select checkbox...
47314             tb.addField( new Roo.form.ComboBox({
47315                 store: new Roo.data.SimpleStore({
47316                     id : 'val',
47317                     fields: ['val', 'selected'],
47318                     data : [] 
47319                 }),
47320                 name : '-roo-edit-className',
47321                 attrname : 'className',
47322                 displayField: 'val',
47323                 typeAhead: false,
47324                 mode: 'local',
47325                 editable : false,
47326                 triggerAction: 'all',
47327                 emptyText:'Select Style',
47328                 selectOnFocus:true,
47329                 width: 130,
47330                 listeners : {
47331                     'select': function(c, r, i) {
47332                         // initial support only for on class per el..
47333                         tb.selectedNode.className =  r ? r.get('val') : '';
47334                         editorcore.syncValue();
47335                     }
47336                 }
47337     
47338             }));
47339         }
47340         
47341         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47342         var tbops = tbc.options;
47343         
47344         for (var i in tlist) {
47345             
47346             var item = tlist[i];
47347             tb.add(item.title + ":&nbsp;");
47348             
47349             
47350             //optname == used so you can configure the options available..
47351             var opts = item.opts ? item.opts : false;
47352             if (item.optname) {
47353                 opts = tbops[item.optname];
47354            
47355             }
47356             
47357             if (opts) {
47358                 // opts == pulldown..
47359                 tb.addField( new Roo.form.ComboBox({
47360                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47361                         id : 'val',
47362                         fields: ['val', 'display'],
47363                         data : opts  
47364                     }),
47365                     name : '-roo-edit-' + i,
47366                     attrname : i,
47367                     stylename : item.style ? item.style : false,
47368                     displayField: item.displayField ? item.displayField : 'val',
47369                     valueField :  'val',
47370                     typeAhead: false,
47371                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47372                     editable : false,
47373                     triggerAction: 'all',
47374                     emptyText:'Select',
47375                     selectOnFocus:true,
47376                     width: item.width ? item.width  : 130,
47377                     listeners : {
47378                         'select': function(c, r, i) {
47379                             if (c.stylename) {
47380                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47381                                 return;
47382                             }
47383                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47384                         }
47385                     }
47386
47387                 }));
47388                 continue;
47389                     
47390                  
47391                 
47392                 tb.addField( new Roo.form.TextField({
47393                     name: i,
47394                     width: 100,
47395                     //allowBlank:false,
47396                     value: ''
47397                 }));
47398                 continue;
47399             }
47400             tb.addField( new Roo.form.TextField({
47401                 name: '-roo-edit-' + i,
47402                 attrname : i,
47403                 
47404                 width: item.width,
47405                 //allowBlank:true,
47406                 value: '',
47407                 listeners: {
47408                     'change' : function(f, nv, ov) {
47409                         tb.selectedNode.setAttribute(f.attrname, nv);
47410                         editorcore.syncValue();
47411                     }
47412                 }
47413             }));
47414              
47415         }
47416         
47417         var _this = this;
47418         
47419         if(nm == 'BODY'){
47420             tb.addSeparator();
47421         
47422             tb.addButton( {
47423                 text: 'Stylesheets',
47424
47425                 listeners : {
47426                     click : function ()
47427                     {
47428                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47429                     }
47430                 }
47431             });
47432         }
47433         
47434         tb.addFill();
47435         tb.addButton( {
47436             text: 'Remove Tag',
47437     
47438             listeners : {
47439                 click : function ()
47440                 {
47441                     // remove
47442                     // undo does not work.
47443                      
47444                     var sn = tb.selectedNode;
47445                     
47446                     var pn = sn.parentNode;
47447                     
47448                     var stn =  sn.childNodes[0];
47449                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47450                     while (sn.childNodes.length) {
47451                         var node = sn.childNodes[0];
47452                         sn.removeChild(node);
47453                         //Roo.log(node);
47454                         pn.insertBefore(node, sn);
47455                         
47456                     }
47457                     pn.removeChild(sn);
47458                     var range = editorcore.createRange();
47459         
47460                     range.setStart(stn,0);
47461                     range.setEnd(en,0); //????
47462                     //range.selectNode(sel);
47463                     
47464                     
47465                     var selection = editorcore.getSelection();
47466                     selection.removeAllRanges();
47467                     selection.addRange(range);
47468                     
47469                     
47470                     
47471                     //_this.updateToolbar(null, null, pn);
47472                     _this.updateToolbar(null, null, null);
47473                     _this.footDisp.dom.innerHTML = ''; 
47474                 }
47475             }
47476             
47477                     
47478                 
47479             
47480         });
47481         
47482         
47483         tb.el.on('click', function(e){
47484             e.preventDefault(); // what does this do?
47485         });
47486         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47487         tb.el.hide();
47488         tb.name = nm;
47489         // dont need to disable them... as they will get hidden
47490         return tb;
47491          
47492         
47493     },
47494     buildFooter : function()
47495     {
47496         
47497         var fel = this.editor.wrap.createChild();
47498         this.footer = new Roo.Toolbar(fel);
47499         // toolbar has scrolly on left / right?
47500         var footDisp= new Roo.Toolbar.Fill();
47501         var _t = this;
47502         this.footer.add(
47503             {
47504                 text : '&lt;',
47505                 xtype: 'Button',
47506                 handler : function() {
47507                     _t.footDisp.scrollTo('left',0,true)
47508                 }
47509             }
47510         );
47511         this.footer.add( footDisp );
47512         this.footer.add( 
47513             {
47514                 text : '&gt;',
47515                 xtype: 'Button',
47516                 handler : function() {
47517                     // no animation..
47518                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47519                 }
47520             }
47521         );
47522         var fel = Roo.get(footDisp.el);
47523         fel.addClass('x-editor-context');
47524         this.footDispWrap = fel; 
47525         this.footDispWrap.overflow  = 'hidden';
47526         
47527         this.footDisp = fel.createChild();
47528         this.footDispWrap.on('click', this.onContextClick, this)
47529         
47530         
47531     },
47532     onContextClick : function (ev,dom)
47533     {
47534         ev.preventDefault();
47535         var  cn = dom.className;
47536         //Roo.log(cn);
47537         if (!cn.match(/x-ed-loc-/)) {
47538             return;
47539         }
47540         var n = cn.split('-').pop();
47541         var ans = this.footerEls;
47542         var sel = ans[n];
47543         
47544          // pick
47545         var range = this.editorcore.createRange();
47546         
47547         range.selectNodeContents(sel);
47548         //range.selectNode(sel);
47549         
47550         
47551         var selection = this.editorcore.getSelection();
47552         selection.removeAllRanges();
47553         selection.addRange(range);
47554         
47555         
47556         
47557         this.updateToolbar(null, null, sel);
47558         
47559         
47560     }
47561     
47562     
47563     
47564     
47565     
47566 });
47567
47568
47569
47570
47571
47572 /*
47573  * Based on:
47574  * Ext JS Library 1.1.1
47575  * Copyright(c) 2006-2007, Ext JS, LLC.
47576  *
47577  * Originally Released Under LGPL - original licence link has changed is not relivant.
47578  *
47579  * Fork - LGPL
47580  * <script type="text/javascript">
47581  */
47582  
47583 /**
47584  * @class Roo.form.BasicForm
47585  * @extends Roo.util.Observable
47586  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47587  * @constructor
47588  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47589  * @param {Object} config Configuration options
47590  */
47591 Roo.form.BasicForm = function(el, config){
47592     this.allItems = [];
47593     this.childForms = [];
47594     Roo.apply(this, config);
47595     /*
47596      * The Roo.form.Field items in this form.
47597      * @type MixedCollection
47598      */
47599      
47600      
47601     this.items = new Roo.util.MixedCollection(false, function(o){
47602         return o.id || (o.id = Roo.id());
47603     });
47604     this.addEvents({
47605         /**
47606          * @event beforeaction
47607          * Fires before any action is performed. Return false to cancel the action.
47608          * @param {Form} this
47609          * @param {Action} action The action to be performed
47610          */
47611         beforeaction: true,
47612         /**
47613          * @event actionfailed
47614          * Fires when an action fails.
47615          * @param {Form} this
47616          * @param {Action} action The action that failed
47617          */
47618         actionfailed : true,
47619         /**
47620          * @event actioncomplete
47621          * Fires when an action is completed.
47622          * @param {Form} this
47623          * @param {Action} action The action that completed
47624          */
47625         actioncomplete : true
47626     });
47627     if(el){
47628         this.initEl(el);
47629     }
47630     Roo.form.BasicForm.superclass.constructor.call(this);
47631     
47632     Roo.form.BasicForm.popover.apply();
47633 };
47634
47635 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47636     /**
47637      * @cfg {String} method
47638      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47639      */
47640     /**
47641      * @cfg {DataReader} reader
47642      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47643      * This is optional as there is built-in support for processing JSON.
47644      */
47645     /**
47646      * @cfg {DataReader} errorReader
47647      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47648      * This is completely optional as there is built-in support for processing JSON.
47649      */
47650     /**
47651      * @cfg {String} url
47652      * The URL to use for form actions if one isn't supplied in the action options.
47653      */
47654     /**
47655      * @cfg {Boolean} fileUpload
47656      * Set to true if this form is a file upload.
47657      */
47658      
47659     /**
47660      * @cfg {Object} baseParams
47661      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47662      */
47663      /**
47664      
47665     /**
47666      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47667      */
47668     timeout: 30,
47669
47670     // private
47671     activeAction : null,
47672
47673     /**
47674      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47675      * or setValues() data instead of when the form was first created.
47676      */
47677     trackResetOnLoad : false,
47678     
47679     
47680     /**
47681      * childForms - used for multi-tab forms
47682      * @type {Array}
47683      */
47684     childForms : false,
47685     
47686     /**
47687      * allItems - full list of fields.
47688      * @type {Array}
47689      */
47690     allItems : false,
47691     
47692     /**
47693      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47694      * element by passing it or its id or mask the form itself by passing in true.
47695      * @type Mixed
47696      */
47697     waitMsgTarget : false,
47698     
47699     /**
47700      * @type Boolean
47701      */
47702     disableMask : false,
47703     
47704     /**
47705      * @cfg {Boolean} errorMask (true|false) default false
47706      */
47707     errorMask : false,
47708     
47709     /**
47710      * @cfg {Number} maskOffset Default 100
47711      */
47712     maskOffset : 100,
47713
47714     // private
47715     initEl : function(el){
47716         this.el = Roo.get(el);
47717         this.id = this.el.id || Roo.id();
47718         this.el.on('submit', this.onSubmit, this);
47719         this.el.addClass('x-form');
47720     },
47721
47722     // private
47723     onSubmit : function(e){
47724         e.stopEvent();
47725     },
47726
47727     /**
47728      * Returns true if client-side validation on the form is successful.
47729      * @return Boolean
47730      */
47731     isValid : function(){
47732         var valid = true;
47733         var target = false;
47734         this.items.each(function(f){
47735             if(f.validate()){
47736                 return;
47737             }
47738             
47739             valid = false;
47740                 
47741             if(!target && f.el.isVisible(true)){
47742                 target = f;
47743             }
47744         });
47745         
47746         if(this.errorMask && !valid){
47747             Roo.form.BasicForm.popover.mask(this, target);
47748         }
47749         
47750         return valid;
47751     },
47752     /**
47753      * Returns array of invalid form fields.
47754      * @return Array
47755      */
47756     
47757     invalidFields : function()
47758     {
47759         var ret = [];
47760         this.items.each(function(f){
47761             if(f.validate()){
47762                 return;
47763             }
47764             ret.push(f);
47765             
47766         });
47767         
47768         return ret;
47769     },
47770     
47771     
47772     /**
47773      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47774      * @return Boolean
47775      */
47776     isDirty : function(){
47777         var dirty = false;
47778         this.items.each(function(f){
47779            if(f.isDirty()){
47780                dirty = true;
47781                return false;
47782            }
47783         });
47784         return dirty;
47785     },
47786     
47787     /**
47788      * Returns true if any fields in this form have changed since their original load. (New version)
47789      * @return Boolean
47790      */
47791     
47792     hasChanged : function()
47793     {
47794         var dirty = false;
47795         this.items.each(function(f){
47796            if(f.hasChanged()){
47797                dirty = true;
47798                return false;
47799            }
47800         });
47801         return dirty;
47802         
47803     },
47804     /**
47805      * Resets all hasChanged to 'false' -
47806      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47807      * So hasChanged storage is only to be used for this purpose
47808      * @return Boolean
47809      */
47810     resetHasChanged : function()
47811     {
47812         this.items.each(function(f){
47813            f.resetHasChanged();
47814         });
47815         
47816     },
47817     
47818     
47819     /**
47820      * Performs a predefined action (submit or load) or custom actions you define on this form.
47821      * @param {String} actionName The name of the action type
47822      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47823      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47824      * accept other config options):
47825      * <pre>
47826 Property          Type             Description
47827 ----------------  ---------------  ----------------------------------------------------------------------------------
47828 url               String           The url for the action (defaults to the form's url)
47829 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47830 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47831 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47832                                    validate the form on the client (defaults to false)
47833      * </pre>
47834      * @return {BasicForm} this
47835      */
47836     doAction : function(action, options){
47837         if(typeof action == 'string'){
47838             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47839         }
47840         if(this.fireEvent('beforeaction', this, action) !== false){
47841             this.beforeAction(action);
47842             action.run.defer(100, action);
47843         }
47844         return this;
47845     },
47846
47847     /**
47848      * Shortcut to do a submit action.
47849      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47850      * @return {BasicForm} this
47851      */
47852     submit : function(options){
47853         this.doAction('submit', options);
47854         return this;
47855     },
47856
47857     /**
47858      * Shortcut to do a load action.
47859      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47860      * @return {BasicForm} this
47861      */
47862     load : function(options){
47863         this.doAction('load', options);
47864         return this;
47865     },
47866
47867     /**
47868      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47869      * @param {Record} record The record to edit
47870      * @return {BasicForm} this
47871      */
47872     updateRecord : function(record){
47873         record.beginEdit();
47874         var fs = record.fields;
47875         fs.each(function(f){
47876             var field = this.findField(f.name);
47877             if(field){
47878                 record.set(f.name, field.getValue());
47879             }
47880         }, this);
47881         record.endEdit();
47882         return this;
47883     },
47884
47885     /**
47886      * Loads an Roo.data.Record into this form.
47887      * @param {Record} record The record to load
47888      * @return {BasicForm} this
47889      */
47890     loadRecord : function(record){
47891         this.setValues(record.data);
47892         return this;
47893     },
47894
47895     // private
47896     beforeAction : function(action){
47897         var o = action.options;
47898         
47899         if(!this.disableMask) {
47900             if(this.waitMsgTarget === true){
47901                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47902             }else if(this.waitMsgTarget){
47903                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47904                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47905             }else {
47906                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47907             }
47908         }
47909         
47910          
47911     },
47912
47913     // private
47914     afterAction : function(action, success){
47915         this.activeAction = null;
47916         var o = action.options;
47917         
47918         if(!this.disableMask) {
47919             if(this.waitMsgTarget === true){
47920                 this.el.unmask();
47921             }else if(this.waitMsgTarget){
47922                 this.waitMsgTarget.unmask();
47923             }else{
47924                 Roo.MessageBox.updateProgress(1);
47925                 Roo.MessageBox.hide();
47926             }
47927         }
47928         
47929         if(success){
47930             if(o.reset){
47931                 this.reset();
47932             }
47933             Roo.callback(o.success, o.scope, [this, action]);
47934             this.fireEvent('actioncomplete', this, action);
47935             
47936         }else{
47937             
47938             // failure condition..
47939             // we have a scenario where updates need confirming.
47940             // eg. if a locking scenario exists..
47941             // we look for { errors : { needs_confirm : true }} in the response.
47942             if (
47943                 (typeof(action.result) != 'undefined')  &&
47944                 (typeof(action.result.errors) != 'undefined')  &&
47945                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47946            ){
47947                 var _t = this;
47948                 Roo.MessageBox.confirm(
47949                     "Change requires confirmation",
47950                     action.result.errorMsg,
47951                     function(r) {
47952                         if (r != 'yes') {
47953                             return;
47954                         }
47955                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47956                     }
47957                     
47958                 );
47959                 
47960                 
47961                 
47962                 return;
47963             }
47964             
47965             Roo.callback(o.failure, o.scope, [this, action]);
47966             // show an error message if no failed handler is set..
47967             if (!this.hasListener('actionfailed')) {
47968                 Roo.MessageBox.alert("Error",
47969                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47970                         action.result.errorMsg :
47971                         "Saving Failed, please check your entries or try again"
47972                 );
47973             }
47974             
47975             this.fireEvent('actionfailed', this, action);
47976         }
47977         
47978     },
47979
47980     /**
47981      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47982      * @param {String} id The value to search for
47983      * @return Field
47984      */
47985     findField : function(id){
47986         var field = this.items.get(id);
47987         if(!field){
47988             this.items.each(function(f){
47989                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47990                     field = f;
47991                     return false;
47992                 }
47993             });
47994         }
47995         return field || null;
47996     },
47997
47998     /**
47999      * Add a secondary form to this one, 
48000      * Used to provide tabbed forms. One form is primary, with hidden values 
48001      * which mirror the elements from the other forms.
48002      * 
48003      * @param {Roo.form.Form} form to add.
48004      * 
48005      */
48006     addForm : function(form)
48007     {
48008        
48009         if (this.childForms.indexOf(form) > -1) {
48010             // already added..
48011             return;
48012         }
48013         this.childForms.push(form);
48014         var n = '';
48015         Roo.each(form.allItems, function (fe) {
48016             
48017             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48018             if (this.findField(n)) { // already added..
48019                 return;
48020             }
48021             var add = new Roo.form.Hidden({
48022                 name : n
48023             });
48024             add.render(this.el);
48025             
48026             this.add( add );
48027         }, this);
48028         
48029     },
48030     /**
48031      * Mark fields in this form invalid in bulk.
48032      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48033      * @return {BasicForm} this
48034      */
48035     markInvalid : function(errors){
48036         if(errors instanceof Array){
48037             for(var i = 0, len = errors.length; i < len; i++){
48038                 var fieldError = errors[i];
48039                 var f = this.findField(fieldError.id);
48040                 if(f){
48041                     f.markInvalid(fieldError.msg);
48042                 }
48043             }
48044         }else{
48045             var field, id;
48046             for(id in errors){
48047                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48048                     field.markInvalid(errors[id]);
48049                 }
48050             }
48051         }
48052         Roo.each(this.childForms || [], function (f) {
48053             f.markInvalid(errors);
48054         });
48055         
48056         return this;
48057     },
48058
48059     /**
48060      * Set values for fields in this form in bulk.
48061      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48062      * @return {BasicForm} this
48063      */
48064     setValues : function(values){
48065         if(values instanceof Array){ // array of objects
48066             for(var i = 0, len = values.length; i < len; i++){
48067                 var v = values[i];
48068                 var f = this.findField(v.id);
48069                 if(f){
48070                     f.setValue(v.value);
48071                     if(this.trackResetOnLoad){
48072                         f.originalValue = f.getValue();
48073                     }
48074                 }
48075             }
48076         }else{ // object hash
48077             var field, id;
48078             for(id in values){
48079                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48080                     
48081                     if (field.setFromData && 
48082                         field.valueField && 
48083                         field.displayField &&
48084                         // combos' with local stores can 
48085                         // be queried via setValue()
48086                         // to set their value..
48087                         (field.store && !field.store.isLocal)
48088                         ) {
48089                         // it's a combo
48090                         var sd = { };
48091                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48092                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48093                         field.setFromData(sd);
48094                         
48095                     } else {
48096                         field.setValue(values[id]);
48097                     }
48098                     
48099                     
48100                     if(this.trackResetOnLoad){
48101                         field.originalValue = field.getValue();
48102                     }
48103                 }
48104             }
48105         }
48106         this.resetHasChanged();
48107         
48108         
48109         Roo.each(this.childForms || [], function (f) {
48110             f.setValues(values);
48111             f.resetHasChanged();
48112         });
48113                 
48114         return this;
48115     },
48116  
48117     /**
48118      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48119      * they are returned as an array.
48120      * @param {Boolean} asString
48121      * @return {Object}
48122      */
48123     getValues : function(asString){
48124         if (this.childForms) {
48125             // copy values from the child forms
48126             Roo.each(this.childForms, function (f) {
48127                 this.setValues(f.getValues());
48128             }, this);
48129         }
48130         
48131         // use formdata
48132         if (typeof(FormData) != 'undefined' && asString !== true) {
48133             // this relies on a 'recent' version of chrome apparently...
48134             try {
48135                 var fd = (new FormData(this.el.dom)).entries();
48136                 var ret = {};
48137                 var ent = fd.next();
48138                 while (!ent.done) {
48139                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48140                     ent = fd.next();
48141                 };
48142                 return ret;
48143             } catch(e) {
48144                 
48145             }
48146             
48147         }
48148         
48149         
48150         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48151         if(asString === true){
48152             return fs;
48153         }
48154         return Roo.urlDecode(fs);
48155     },
48156     
48157     /**
48158      * Returns the fields in this form as an object with key/value pairs. 
48159      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48160      * @return {Object}
48161      */
48162     getFieldValues : function(with_hidden)
48163     {
48164         if (this.childForms) {
48165             // copy values from the child forms
48166             // should this call getFieldValues - probably not as we do not currently copy
48167             // hidden fields when we generate..
48168             Roo.each(this.childForms, function (f) {
48169                 this.setValues(f.getValues());
48170             }, this);
48171         }
48172         
48173         var ret = {};
48174         this.items.each(function(f){
48175             if (!f.getName()) {
48176                 return;
48177             }
48178             var v = f.getValue();
48179             if (f.inputType =='radio') {
48180                 if (typeof(ret[f.getName()]) == 'undefined') {
48181                     ret[f.getName()] = ''; // empty..
48182                 }
48183                 
48184                 if (!f.el.dom.checked) {
48185                     return;
48186                     
48187                 }
48188                 v = f.el.dom.value;
48189                 
48190             }
48191             
48192             // not sure if this supported any more..
48193             if ((typeof(v) == 'object') && f.getRawValue) {
48194                 v = f.getRawValue() ; // dates..
48195             }
48196             // combo boxes where name != hiddenName...
48197             if (f.name != f.getName()) {
48198                 ret[f.name] = f.getRawValue();
48199             }
48200             ret[f.getName()] = v;
48201         });
48202         
48203         return ret;
48204     },
48205
48206     /**
48207      * Clears all invalid messages in this form.
48208      * @return {BasicForm} this
48209      */
48210     clearInvalid : function(){
48211         this.items.each(function(f){
48212            f.clearInvalid();
48213         });
48214         
48215         Roo.each(this.childForms || [], function (f) {
48216             f.clearInvalid();
48217         });
48218         
48219         
48220         return this;
48221     },
48222
48223     /**
48224      * Resets this form.
48225      * @return {BasicForm} this
48226      */
48227     reset : function(){
48228         this.items.each(function(f){
48229             f.reset();
48230         });
48231         
48232         Roo.each(this.childForms || [], function (f) {
48233             f.reset();
48234         });
48235         this.resetHasChanged();
48236         
48237         return this;
48238     },
48239
48240     /**
48241      * Add Roo.form components to this form.
48242      * @param {Field} field1
48243      * @param {Field} field2 (optional)
48244      * @param {Field} etc (optional)
48245      * @return {BasicForm} this
48246      */
48247     add : function(){
48248         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48249         return this;
48250     },
48251
48252
48253     /**
48254      * Removes a field from the items collection (does NOT remove its markup).
48255      * @param {Field} field
48256      * @return {BasicForm} this
48257      */
48258     remove : function(field){
48259         this.items.remove(field);
48260         return this;
48261     },
48262
48263     /**
48264      * Looks at the fields in this form, checks them for an id attribute,
48265      * and calls applyTo on the existing dom element with that id.
48266      * @return {BasicForm} this
48267      */
48268     render : function(){
48269         this.items.each(function(f){
48270             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48271                 f.applyTo(f.id);
48272             }
48273         });
48274         return this;
48275     },
48276
48277     /**
48278      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48279      * @param {Object} values
48280      * @return {BasicForm} this
48281      */
48282     applyToFields : function(o){
48283         this.items.each(function(f){
48284            Roo.apply(f, o);
48285         });
48286         return this;
48287     },
48288
48289     /**
48290      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48291      * @param {Object} values
48292      * @return {BasicForm} this
48293      */
48294     applyIfToFields : function(o){
48295         this.items.each(function(f){
48296            Roo.applyIf(f, o);
48297         });
48298         return this;
48299     }
48300 });
48301
48302 // back compat
48303 Roo.BasicForm = Roo.form.BasicForm;
48304
48305 Roo.apply(Roo.form.BasicForm, {
48306     
48307     popover : {
48308         
48309         padding : 5,
48310         
48311         isApplied : false,
48312         
48313         isMasked : false,
48314         
48315         form : false,
48316         
48317         target : false,
48318         
48319         intervalID : false,
48320         
48321         maskEl : false,
48322         
48323         apply : function()
48324         {
48325             if(this.isApplied){
48326                 return;
48327             }
48328             
48329             this.maskEl = {
48330                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48331                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48332                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48333                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48334             };
48335             
48336             this.maskEl.top.enableDisplayMode("block");
48337             this.maskEl.left.enableDisplayMode("block");
48338             this.maskEl.bottom.enableDisplayMode("block");
48339             this.maskEl.right.enableDisplayMode("block");
48340             
48341             Roo.get(document.body).on('click', function(){
48342                 this.unmask();
48343             }, this);
48344             
48345             Roo.get(document.body).on('touchstart', function(){
48346                 this.unmask();
48347             }, this);
48348             
48349             this.isApplied = true
48350         },
48351         
48352         mask : function(form, target)
48353         {
48354             this.form = form;
48355             
48356             this.target = target;
48357             
48358             if(!this.form.errorMask || !target.el){
48359                 return;
48360             }
48361             
48362             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48363             
48364             var ot = this.target.el.calcOffsetsTo(scrollable);
48365             
48366             var scrollTo = ot[1] - this.form.maskOffset;
48367             
48368             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48369             
48370             scrollable.scrollTo('top', scrollTo);
48371             
48372             var el = this.target.wrap || this.target.el;
48373             
48374             var box = el.getBox();
48375             
48376             this.maskEl.top.setStyle('position', 'absolute');
48377             this.maskEl.top.setStyle('z-index', 10000);
48378             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48379             this.maskEl.top.setLeft(0);
48380             this.maskEl.top.setTop(0);
48381             this.maskEl.top.show();
48382             
48383             this.maskEl.left.setStyle('position', 'absolute');
48384             this.maskEl.left.setStyle('z-index', 10000);
48385             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48386             this.maskEl.left.setLeft(0);
48387             this.maskEl.left.setTop(box.y - this.padding);
48388             this.maskEl.left.show();
48389
48390             this.maskEl.bottom.setStyle('position', 'absolute');
48391             this.maskEl.bottom.setStyle('z-index', 10000);
48392             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48393             this.maskEl.bottom.setLeft(0);
48394             this.maskEl.bottom.setTop(box.bottom + this.padding);
48395             this.maskEl.bottom.show();
48396
48397             this.maskEl.right.setStyle('position', 'absolute');
48398             this.maskEl.right.setStyle('z-index', 10000);
48399             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48400             this.maskEl.right.setLeft(box.right + this.padding);
48401             this.maskEl.right.setTop(box.y - this.padding);
48402             this.maskEl.right.show();
48403
48404             this.intervalID = window.setInterval(function() {
48405                 Roo.form.BasicForm.popover.unmask();
48406             }, 10000);
48407
48408             window.onwheel = function(){ return false;};
48409             
48410             (function(){ this.isMasked = true; }).defer(500, this);
48411             
48412         },
48413         
48414         unmask : function()
48415         {
48416             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48417                 return;
48418             }
48419             
48420             this.maskEl.top.setStyle('position', 'absolute');
48421             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48422             this.maskEl.top.hide();
48423
48424             this.maskEl.left.setStyle('position', 'absolute');
48425             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48426             this.maskEl.left.hide();
48427
48428             this.maskEl.bottom.setStyle('position', 'absolute');
48429             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48430             this.maskEl.bottom.hide();
48431
48432             this.maskEl.right.setStyle('position', 'absolute');
48433             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48434             this.maskEl.right.hide();
48435             
48436             window.onwheel = function(){ return true;};
48437             
48438             if(this.intervalID){
48439                 window.clearInterval(this.intervalID);
48440                 this.intervalID = false;
48441             }
48442             
48443             this.isMasked = false;
48444             
48445         }
48446         
48447     }
48448     
48449 });/*
48450  * Based on:
48451  * Ext JS Library 1.1.1
48452  * Copyright(c) 2006-2007, Ext JS, LLC.
48453  *
48454  * Originally Released Under LGPL - original licence link has changed is not relivant.
48455  *
48456  * Fork - LGPL
48457  * <script type="text/javascript">
48458  */
48459
48460 /**
48461  * @class Roo.form.Form
48462  * @extends Roo.form.BasicForm
48463  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48464  * @constructor
48465  * @param {Object} config Configuration options
48466  */
48467 Roo.form.Form = function(config){
48468     var xitems =  [];
48469     if (config.items) {
48470         xitems = config.items;
48471         delete config.items;
48472     }
48473    
48474     
48475     Roo.form.Form.superclass.constructor.call(this, null, config);
48476     this.url = this.url || this.action;
48477     if(!this.root){
48478         this.root = new Roo.form.Layout(Roo.applyIf({
48479             id: Roo.id()
48480         }, config));
48481     }
48482     this.active = this.root;
48483     /**
48484      * Array of all the buttons that have been added to this form via {@link addButton}
48485      * @type Array
48486      */
48487     this.buttons = [];
48488     this.allItems = [];
48489     this.addEvents({
48490         /**
48491          * @event clientvalidation
48492          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48493          * @param {Form} this
48494          * @param {Boolean} valid true if the form has passed client-side validation
48495          */
48496         clientvalidation: true,
48497         /**
48498          * @event rendered
48499          * Fires when the form is rendered
48500          * @param {Roo.form.Form} form
48501          */
48502         rendered : true
48503     });
48504     
48505     if (this.progressUrl) {
48506             // push a hidden field onto the list of fields..
48507             this.addxtype( {
48508                     xns: Roo.form, 
48509                     xtype : 'Hidden', 
48510                     name : 'UPLOAD_IDENTIFIER' 
48511             });
48512         }
48513         
48514     
48515     Roo.each(xitems, this.addxtype, this);
48516     
48517 };
48518
48519 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48520     /**
48521      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48522      */
48523     /**
48524      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48525      */
48526     /**
48527      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48528      */
48529     buttonAlign:'center',
48530
48531     /**
48532      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48533      */
48534     minButtonWidth:75,
48535
48536     /**
48537      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48538      * This property cascades to child containers if not set.
48539      */
48540     labelAlign:'left',
48541
48542     /**
48543      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48544      * fires a looping event with that state. This is required to bind buttons to the valid
48545      * state using the config value formBind:true on the button.
48546      */
48547     monitorValid : false,
48548
48549     /**
48550      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48551      */
48552     monitorPoll : 200,
48553     
48554     /**
48555      * @cfg {String} progressUrl - Url to return progress data 
48556      */
48557     
48558     progressUrl : false,
48559     /**
48560      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48561      * sending a formdata with extra parameters - eg uploaded elements.
48562      */
48563     
48564     formData : false,
48565     
48566     /**
48567      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48568      * fields are added and the column is closed. If no fields are passed the column remains open
48569      * until end() is called.
48570      * @param {Object} config The config to pass to the column
48571      * @param {Field} field1 (optional)
48572      * @param {Field} field2 (optional)
48573      * @param {Field} etc (optional)
48574      * @return Column The column container object
48575      */
48576     column : function(c){
48577         var col = new Roo.form.Column(c);
48578         this.start(col);
48579         if(arguments.length > 1){ // duplicate code required because of Opera
48580             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48581             this.end();
48582         }
48583         return col;
48584     },
48585
48586     /**
48587      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48588      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48589      * until end() is called.
48590      * @param {Object} config The config to pass to the fieldset
48591      * @param {Field} field1 (optional)
48592      * @param {Field} field2 (optional)
48593      * @param {Field} etc (optional)
48594      * @return FieldSet The fieldset container object
48595      */
48596     fieldset : function(c){
48597         var fs = new Roo.form.FieldSet(c);
48598         this.start(fs);
48599         if(arguments.length > 1){ // duplicate code required because of Opera
48600             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48601             this.end();
48602         }
48603         return fs;
48604     },
48605
48606     /**
48607      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48608      * fields are added and the container is closed. If no fields are passed the container remains open
48609      * until end() is called.
48610      * @param {Object} config The config to pass to the Layout
48611      * @param {Field} field1 (optional)
48612      * @param {Field} field2 (optional)
48613      * @param {Field} etc (optional)
48614      * @return Layout The container object
48615      */
48616     container : function(c){
48617         var l = new Roo.form.Layout(c);
48618         this.start(l);
48619         if(arguments.length > 1){ // duplicate code required because of Opera
48620             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48621             this.end();
48622         }
48623         return l;
48624     },
48625
48626     /**
48627      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48628      * @param {Object} container A Roo.form.Layout or subclass of Layout
48629      * @return {Form} this
48630      */
48631     start : function(c){
48632         // cascade label info
48633         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48634         this.active.stack.push(c);
48635         c.ownerCt = this.active;
48636         this.active = c;
48637         return this;
48638     },
48639
48640     /**
48641      * Closes the current open container
48642      * @return {Form} this
48643      */
48644     end : function(){
48645         if(this.active == this.root){
48646             return this;
48647         }
48648         this.active = this.active.ownerCt;
48649         return this;
48650     },
48651
48652     /**
48653      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48654      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48655      * as the label of the field.
48656      * @param {Field} field1
48657      * @param {Field} field2 (optional)
48658      * @param {Field} etc. (optional)
48659      * @return {Form} this
48660      */
48661     add : function(){
48662         this.active.stack.push.apply(this.active.stack, arguments);
48663         this.allItems.push.apply(this.allItems,arguments);
48664         var r = [];
48665         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48666             if(a[i].isFormField){
48667                 r.push(a[i]);
48668             }
48669         }
48670         if(r.length > 0){
48671             Roo.form.Form.superclass.add.apply(this, r);
48672         }
48673         return this;
48674     },
48675     
48676
48677     
48678     
48679     
48680      /**
48681      * Find any element that has been added to a form, using it's ID or name
48682      * This can include framesets, columns etc. along with regular fields..
48683      * @param {String} id - id or name to find.
48684      
48685      * @return {Element} e - or false if nothing found.
48686      */
48687     findbyId : function(id)
48688     {
48689         var ret = false;
48690         if (!id) {
48691             return ret;
48692         }
48693         Roo.each(this.allItems, function(f){
48694             if (f.id == id || f.name == id ){
48695                 ret = f;
48696                 return false;
48697             }
48698         });
48699         return ret;
48700     },
48701
48702     
48703     
48704     /**
48705      * Render this form into the passed container. This should only be called once!
48706      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48707      * @return {Form} this
48708      */
48709     render : function(ct)
48710     {
48711         
48712         
48713         
48714         ct = Roo.get(ct);
48715         var o = this.autoCreate || {
48716             tag: 'form',
48717             method : this.method || 'POST',
48718             id : this.id || Roo.id()
48719         };
48720         this.initEl(ct.createChild(o));
48721
48722         this.root.render(this.el);
48723         
48724        
48725              
48726         this.items.each(function(f){
48727             f.render('x-form-el-'+f.id);
48728         });
48729
48730         if(this.buttons.length > 0){
48731             // tables are required to maintain order and for correct IE layout
48732             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48733                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48734                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48735             }}, null, true);
48736             var tr = tb.getElementsByTagName('tr')[0];
48737             for(var i = 0, len = this.buttons.length; i < len; i++) {
48738                 var b = this.buttons[i];
48739                 var td = document.createElement('td');
48740                 td.className = 'x-form-btn-td';
48741                 b.render(tr.appendChild(td));
48742             }
48743         }
48744         if(this.monitorValid){ // initialize after render
48745             this.startMonitoring();
48746         }
48747         this.fireEvent('rendered', this);
48748         return this;
48749     },
48750
48751     /**
48752      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48753      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48754      * object or a valid Roo.DomHelper element config
48755      * @param {Function} handler The function called when the button is clicked
48756      * @param {Object} scope (optional) The scope of the handler function
48757      * @return {Roo.Button}
48758      */
48759     addButton : function(config, handler, scope){
48760         var bc = {
48761             handler: handler,
48762             scope: scope,
48763             minWidth: this.minButtonWidth,
48764             hideParent:true
48765         };
48766         if(typeof config == "string"){
48767             bc.text = config;
48768         }else{
48769             Roo.apply(bc, config);
48770         }
48771         var btn = new Roo.Button(null, bc);
48772         this.buttons.push(btn);
48773         return btn;
48774     },
48775
48776      /**
48777      * Adds a series of form elements (using the xtype property as the factory method.
48778      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48779      * @param {Object} config 
48780      */
48781     
48782     addxtype : function()
48783     {
48784         var ar = Array.prototype.slice.call(arguments, 0);
48785         var ret = false;
48786         for(var i = 0; i < ar.length; i++) {
48787             if (!ar[i]) {
48788                 continue; // skip -- if this happends something invalid got sent, we 
48789                 // should ignore it, as basically that interface element will not show up
48790                 // and that should be pretty obvious!!
48791             }
48792             
48793             if (Roo.form[ar[i].xtype]) {
48794                 ar[i].form = this;
48795                 var fe = Roo.factory(ar[i], Roo.form);
48796                 if (!ret) {
48797                     ret = fe;
48798                 }
48799                 fe.form = this;
48800                 if (fe.store) {
48801                     fe.store.form = this;
48802                 }
48803                 if (fe.isLayout) {  
48804                          
48805                     this.start(fe);
48806                     this.allItems.push(fe);
48807                     if (fe.items && fe.addxtype) {
48808                         fe.addxtype.apply(fe, fe.items);
48809                         delete fe.items;
48810                     }
48811                      this.end();
48812                     continue;
48813                 }
48814                 
48815                 
48816                  
48817                 this.add(fe);
48818               //  console.log('adding ' + ar[i].xtype);
48819             }
48820             if (ar[i].xtype == 'Button') {  
48821                 //console.log('adding button');
48822                 //console.log(ar[i]);
48823                 this.addButton(ar[i]);
48824                 this.allItems.push(fe);
48825                 continue;
48826             }
48827             
48828             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48829                 alert('end is not supported on xtype any more, use items');
48830             //    this.end();
48831             //    //console.log('adding end');
48832             }
48833             
48834         }
48835         return ret;
48836     },
48837     
48838     /**
48839      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48840      * option "monitorValid"
48841      */
48842     startMonitoring : function(){
48843         if(!this.bound){
48844             this.bound = true;
48845             Roo.TaskMgr.start({
48846                 run : this.bindHandler,
48847                 interval : this.monitorPoll || 200,
48848                 scope: this
48849             });
48850         }
48851     },
48852
48853     /**
48854      * Stops monitoring of the valid state of this form
48855      */
48856     stopMonitoring : function(){
48857         this.bound = false;
48858     },
48859
48860     // private
48861     bindHandler : function(){
48862         if(!this.bound){
48863             return false; // stops binding
48864         }
48865         var valid = true;
48866         this.items.each(function(f){
48867             if(!f.isValid(true)){
48868                 valid = false;
48869                 return false;
48870             }
48871         });
48872         for(var i = 0, len = this.buttons.length; i < len; i++){
48873             var btn = this.buttons[i];
48874             if(btn.formBind === true && btn.disabled === valid){
48875                 btn.setDisabled(!valid);
48876             }
48877         }
48878         this.fireEvent('clientvalidation', this, valid);
48879     }
48880     
48881     
48882     
48883     
48884     
48885     
48886     
48887     
48888 });
48889
48890
48891 // back compat
48892 Roo.Form = Roo.form.Form;
48893 /*
48894  * Based on:
48895  * Ext JS Library 1.1.1
48896  * Copyright(c) 2006-2007, Ext JS, LLC.
48897  *
48898  * Originally Released Under LGPL - original licence link has changed is not relivant.
48899  *
48900  * Fork - LGPL
48901  * <script type="text/javascript">
48902  */
48903
48904 // as we use this in bootstrap.
48905 Roo.namespace('Roo.form');
48906  /**
48907  * @class Roo.form.Action
48908  * Internal Class used to handle form actions
48909  * @constructor
48910  * @param {Roo.form.BasicForm} el The form element or its id
48911  * @param {Object} config Configuration options
48912  */
48913
48914  
48915  
48916 // define the action interface
48917 Roo.form.Action = function(form, options){
48918     this.form = form;
48919     this.options = options || {};
48920 };
48921 /**
48922  * Client Validation Failed
48923  * @const 
48924  */
48925 Roo.form.Action.CLIENT_INVALID = 'client';
48926 /**
48927  * Server Validation Failed
48928  * @const 
48929  */
48930 Roo.form.Action.SERVER_INVALID = 'server';
48931  /**
48932  * Connect to Server Failed
48933  * @const 
48934  */
48935 Roo.form.Action.CONNECT_FAILURE = 'connect';
48936 /**
48937  * Reading Data from Server Failed
48938  * @const 
48939  */
48940 Roo.form.Action.LOAD_FAILURE = 'load';
48941
48942 Roo.form.Action.prototype = {
48943     type : 'default',
48944     failureType : undefined,
48945     response : undefined,
48946     result : undefined,
48947
48948     // interface method
48949     run : function(options){
48950
48951     },
48952
48953     // interface method
48954     success : function(response){
48955
48956     },
48957
48958     // interface method
48959     handleResponse : function(response){
48960
48961     },
48962
48963     // default connection failure
48964     failure : function(response){
48965         
48966         this.response = response;
48967         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48968         this.form.afterAction(this, false);
48969     },
48970
48971     processResponse : function(response){
48972         this.response = response;
48973         if(!response.responseText){
48974             return true;
48975         }
48976         this.result = this.handleResponse(response);
48977         return this.result;
48978     },
48979
48980     // utility functions used internally
48981     getUrl : function(appendParams){
48982         var url = this.options.url || this.form.url || this.form.el.dom.action;
48983         if(appendParams){
48984             var p = this.getParams();
48985             if(p){
48986                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48987             }
48988         }
48989         return url;
48990     },
48991
48992     getMethod : function(){
48993         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48994     },
48995
48996     getParams : function(){
48997         var bp = this.form.baseParams;
48998         var p = this.options.params;
48999         if(p){
49000             if(typeof p == "object"){
49001                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49002             }else if(typeof p == 'string' && bp){
49003                 p += '&' + Roo.urlEncode(bp);
49004             }
49005         }else if(bp){
49006             p = Roo.urlEncode(bp);
49007         }
49008         return p;
49009     },
49010
49011     createCallback : function(){
49012         return {
49013             success: this.success,
49014             failure: this.failure,
49015             scope: this,
49016             timeout: (this.form.timeout*1000),
49017             upload: this.form.fileUpload ? this.success : undefined
49018         };
49019     }
49020 };
49021
49022 Roo.form.Action.Submit = function(form, options){
49023     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49024 };
49025
49026 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49027     type : 'submit',
49028
49029     haveProgress : false,
49030     uploadComplete : false,
49031     
49032     // uploadProgress indicator.
49033     uploadProgress : function()
49034     {
49035         if (!this.form.progressUrl) {
49036             return;
49037         }
49038         
49039         if (!this.haveProgress) {
49040             Roo.MessageBox.progress("Uploading", "Uploading");
49041         }
49042         if (this.uploadComplete) {
49043            Roo.MessageBox.hide();
49044            return;
49045         }
49046         
49047         this.haveProgress = true;
49048    
49049         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49050         
49051         var c = new Roo.data.Connection();
49052         c.request({
49053             url : this.form.progressUrl,
49054             params: {
49055                 id : uid
49056             },
49057             method: 'GET',
49058             success : function(req){
49059                //console.log(data);
49060                 var rdata = false;
49061                 var edata;
49062                 try  {
49063                    rdata = Roo.decode(req.responseText)
49064                 } catch (e) {
49065                     Roo.log("Invalid data from server..");
49066                     Roo.log(edata);
49067                     return;
49068                 }
49069                 if (!rdata || !rdata.success) {
49070                     Roo.log(rdata);
49071                     Roo.MessageBox.alert(Roo.encode(rdata));
49072                     return;
49073                 }
49074                 var data = rdata.data;
49075                 
49076                 if (this.uploadComplete) {
49077                    Roo.MessageBox.hide();
49078                    return;
49079                 }
49080                    
49081                 if (data){
49082                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49083                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49084                     );
49085                 }
49086                 this.uploadProgress.defer(2000,this);
49087             },
49088        
49089             failure: function(data) {
49090                 Roo.log('progress url failed ');
49091                 Roo.log(data);
49092             },
49093             scope : this
49094         });
49095            
49096     },
49097     
49098     
49099     run : function()
49100     {
49101         // run get Values on the form, so it syncs any secondary forms.
49102         this.form.getValues();
49103         
49104         var o = this.options;
49105         var method = this.getMethod();
49106         var isPost = method == 'POST';
49107         if(o.clientValidation === false || this.form.isValid()){
49108             
49109             if (this.form.progressUrl) {
49110                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49111                     (new Date() * 1) + '' + Math.random());
49112                     
49113             } 
49114             
49115             
49116             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49117                 form:this.form.el.dom,
49118                 url:this.getUrl(!isPost),
49119                 method: method,
49120                 params:isPost ? this.getParams() : null,
49121                 isUpload: this.form.fileUpload,
49122                 formData : this.form.formData
49123             }));
49124             
49125             this.uploadProgress();
49126
49127         }else if (o.clientValidation !== false){ // client validation failed
49128             this.failureType = Roo.form.Action.CLIENT_INVALID;
49129             this.form.afterAction(this, false);
49130         }
49131     },
49132
49133     success : function(response)
49134     {
49135         this.uploadComplete= true;
49136         if (this.haveProgress) {
49137             Roo.MessageBox.hide();
49138         }
49139         
49140         
49141         var result = this.processResponse(response);
49142         if(result === true || result.success){
49143             this.form.afterAction(this, true);
49144             return;
49145         }
49146         if(result.errors){
49147             this.form.markInvalid(result.errors);
49148             this.failureType = Roo.form.Action.SERVER_INVALID;
49149         }
49150         this.form.afterAction(this, false);
49151     },
49152     failure : function(response)
49153     {
49154         this.uploadComplete= true;
49155         if (this.haveProgress) {
49156             Roo.MessageBox.hide();
49157         }
49158         
49159         this.response = response;
49160         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49161         this.form.afterAction(this, false);
49162     },
49163     
49164     handleResponse : function(response){
49165         if(this.form.errorReader){
49166             var rs = this.form.errorReader.read(response);
49167             var errors = [];
49168             if(rs.records){
49169                 for(var i = 0, len = rs.records.length; i < len; i++) {
49170                     var r = rs.records[i];
49171                     errors[i] = r.data;
49172                 }
49173             }
49174             if(errors.length < 1){
49175                 errors = null;
49176             }
49177             return {
49178                 success : rs.success,
49179                 errors : errors
49180             };
49181         }
49182         var ret = false;
49183         try {
49184             ret = Roo.decode(response.responseText);
49185         } catch (e) {
49186             ret = {
49187                 success: false,
49188                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49189                 errors : []
49190             };
49191         }
49192         return ret;
49193         
49194     }
49195 });
49196
49197
49198 Roo.form.Action.Load = function(form, options){
49199     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49200     this.reader = this.form.reader;
49201 };
49202
49203 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49204     type : 'load',
49205
49206     run : function(){
49207         
49208         Roo.Ajax.request(Roo.apply(
49209                 this.createCallback(), {
49210                     method:this.getMethod(),
49211                     url:this.getUrl(false),
49212                     params:this.getParams()
49213         }));
49214     },
49215
49216     success : function(response){
49217         
49218         var result = this.processResponse(response);
49219         if(result === true || !result.success || !result.data){
49220             this.failureType = Roo.form.Action.LOAD_FAILURE;
49221             this.form.afterAction(this, false);
49222             return;
49223         }
49224         this.form.clearInvalid();
49225         this.form.setValues(result.data);
49226         this.form.afterAction(this, true);
49227     },
49228
49229     handleResponse : function(response){
49230         if(this.form.reader){
49231             var rs = this.form.reader.read(response);
49232             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49233             return {
49234                 success : rs.success,
49235                 data : data
49236             };
49237         }
49238         return Roo.decode(response.responseText);
49239     }
49240 });
49241
49242 Roo.form.Action.ACTION_TYPES = {
49243     'load' : Roo.form.Action.Load,
49244     'submit' : Roo.form.Action.Submit
49245 };/*
49246  * Based on:
49247  * Ext JS Library 1.1.1
49248  * Copyright(c) 2006-2007, Ext JS, LLC.
49249  *
49250  * Originally Released Under LGPL - original licence link has changed is not relivant.
49251  *
49252  * Fork - LGPL
49253  * <script type="text/javascript">
49254  */
49255  
49256 /**
49257  * @class Roo.form.Layout
49258  * @extends Roo.Component
49259  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49260  * @constructor
49261  * @param {Object} config Configuration options
49262  */
49263 Roo.form.Layout = function(config){
49264     var xitems = [];
49265     if (config.items) {
49266         xitems = config.items;
49267         delete config.items;
49268     }
49269     Roo.form.Layout.superclass.constructor.call(this, config);
49270     this.stack = [];
49271     Roo.each(xitems, this.addxtype, this);
49272      
49273 };
49274
49275 Roo.extend(Roo.form.Layout, Roo.Component, {
49276     /**
49277      * @cfg {String/Object} autoCreate
49278      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49279      */
49280     /**
49281      * @cfg {String/Object/Function} style
49282      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49283      * a function which returns such a specification.
49284      */
49285     /**
49286      * @cfg {String} labelAlign
49287      * Valid values are "left," "top" and "right" (defaults to "left")
49288      */
49289     /**
49290      * @cfg {Number} labelWidth
49291      * Fixed width in pixels of all field labels (defaults to undefined)
49292      */
49293     /**
49294      * @cfg {Boolean} clear
49295      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49296      */
49297     clear : true,
49298     /**
49299      * @cfg {String} labelSeparator
49300      * The separator to use after field labels (defaults to ':')
49301      */
49302     labelSeparator : ':',
49303     /**
49304      * @cfg {Boolean} hideLabels
49305      * True to suppress the display of field labels in this layout (defaults to false)
49306      */
49307     hideLabels : false,
49308
49309     // private
49310     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49311     
49312     isLayout : true,
49313     
49314     // private
49315     onRender : function(ct, position){
49316         if(this.el){ // from markup
49317             this.el = Roo.get(this.el);
49318         }else {  // generate
49319             var cfg = this.getAutoCreate();
49320             this.el = ct.createChild(cfg, position);
49321         }
49322         if(this.style){
49323             this.el.applyStyles(this.style);
49324         }
49325         if(this.labelAlign){
49326             this.el.addClass('x-form-label-'+this.labelAlign);
49327         }
49328         if(this.hideLabels){
49329             this.labelStyle = "display:none";
49330             this.elementStyle = "padding-left:0;";
49331         }else{
49332             if(typeof this.labelWidth == 'number'){
49333                 this.labelStyle = "width:"+this.labelWidth+"px;";
49334                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49335             }
49336             if(this.labelAlign == 'top'){
49337                 this.labelStyle = "width:auto;";
49338                 this.elementStyle = "padding-left:0;";
49339             }
49340         }
49341         var stack = this.stack;
49342         var slen = stack.length;
49343         if(slen > 0){
49344             if(!this.fieldTpl){
49345                 var t = new Roo.Template(
49346                     '<div class="x-form-item {5}">',
49347                         '<label for="{0}" style="{2}">{1}{4}</label>',
49348                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49349                         '</div>',
49350                     '</div><div class="x-form-clear-left"></div>'
49351                 );
49352                 t.disableFormats = true;
49353                 t.compile();
49354                 Roo.form.Layout.prototype.fieldTpl = t;
49355             }
49356             for(var i = 0; i < slen; i++) {
49357                 if(stack[i].isFormField){
49358                     this.renderField(stack[i]);
49359                 }else{
49360                     this.renderComponent(stack[i]);
49361                 }
49362             }
49363         }
49364         if(this.clear){
49365             this.el.createChild({cls:'x-form-clear'});
49366         }
49367     },
49368
49369     // private
49370     renderField : function(f){
49371         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49372                f.id, //0
49373                f.fieldLabel, //1
49374                f.labelStyle||this.labelStyle||'', //2
49375                this.elementStyle||'', //3
49376                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49377                f.itemCls||this.itemCls||''  //5
49378        ], true).getPrevSibling());
49379     },
49380
49381     // private
49382     renderComponent : function(c){
49383         c.render(c.isLayout ? this.el : this.el.createChild());    
49384     },
49385     /**
49386      * Adds a object form elements (using the xtype property as the factory method.)
49387      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49388      * @param {Object} config 
49389      */
49390     addxtype : function(o)
49391     {
49392         // create the lement.
49393         o.form = this.form;
49394         var fe = Roo.factory(o, Roo.form);
49395         this.form.allItems.push(fe);
49396         this.stack.push(fe);
49397         
49398         if (fe.isFormField) {
49399             this.form.items.add(fe);
49400         }
49401          
49402         return fe;
49403     }
49404 });
49405
49406 /**
49407  * @class Roo.form.Column
49408  * @extends Roo.form.Layout
49409  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49410  * @constructor
49411  * @param {Object} config Configuration options
49412  */
49413 Roo.form.Column = function(config){
49414     Roo.form.Column.superclass.constructor.call(this, config);
49415 };
49416
49417 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49418     /**
49419      * @cfg {Number/String} width
49420      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49421      */
49422     /**
49423      * @cfg {String/Object} autoCreate
49424      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49425      */
49426
49427     // private
49428     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49429
49430     // private
49431     onRender : function(ct, position){
49432         Roo.form.Column.superclass.onRender.call(this, ct, position);
49433         if(this.width){
49434             this.el.setWidth(this.width);
49435         }
49436     }
49437 });
49438
49439
49440 /**
49441  * @class Roo.form.Row
49442  * @extends Roo.form.Layout
49443  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49444  * @constructor
49445  * @param {Object} config Configuration options
49446  */
49447
49448  
49449 Roo.form.Row = function(config){
49450     Roo.form.Row.superclass.constructor.call(this, config);
49451 };
49452  
49453 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49454       /**
49455      * @cfg {Number/String} width
49456      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49457      */
49458     /**
49459      * @cfg {Number/String} height
49460      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49461      */
49462     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49463     
49464     padWidth : 20,
49465     // private
49466     onRender : function(ct, position){
49467         //console.log('row render');
49468         if(!this.rowTpl){
49469             var t = new Roo.Template(
49470                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49471                     '<label for="{0}" style="{2}">{1}{4}</label>',
49472                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49473                     '</div>',
49474                 '</div>'
49475             );
49476             t.disableFormats = true;
49477             t.compile();
49478             Roo.form.Layout.prototype.rowTpl = t;
49479         }
49480         this.fieldTpl = this.rowTpl;
49481         
49482         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49483         var labelWidth = 100;
49484         
49485         if ((this.labelAlign != 'top')) {
49486             if (typeof this.labelWidth == 'number') {
49487                 labelWidth = this.labelWidth
49488             }
49489             this.padWidth =  20 + labelWidth;
49490             
49491         }
49492         
49493         Roo.form.Column.superclass.onRender.call(this, ct, position);
49494         if(this.width){
49495             this.el.setWidth(this.width);
49496         }
49497         if(this.height){
49498             this.el.setHeight(this.height);
49499         }
49500     },
49501     
49502     // private
49503     renderField : function(f){
49504         f.fieldEl = this.fieldTpl.append(this.el, [
49505                f.id, f.fieldLabel,
49506                f.labelStyle||this.labelStyle||'',
49507                this.elementStyle||'',
49508                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49509                f.itemCls||this.itemCls||'',
49510                f.width ? f.width + this.padWidth : 160 + this.padWidth
49511        ],true);
49512     }
49513 });
49514  
49515
49516 /**
49517  * @class Roo.form.FieldSet
49518  * @extends Roo.form.Layout
49519  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49520  * @constructor
49521  * @param {Object} config Configuration options
49522  */
49523 Roo.form.FieldSet = function(config){
49524     Roo.form.FieldSet.superclass.constructor.call(this, config);
49525 };
49526
49527 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49528     /**
49529      * @cfg {String} legend
49530      * The text to display as the legend for the FieldSet (defaults to '')
49531      */
49532     /**
49533      * @cfg {String/Object} autoCreate
49534      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49535      */
49536
49537     // private
49538     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49539
49540     // private
49541     onRender : function(ct, position){
49542         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49543         if(this.legend){
49544             this.setLegend(this.legend);
49545         }
49546     },
49547
49548     // private
49549     setLegend : function(text){
49550         if(this.rendered){
49551             this.el.child('legend').update(text);
49552         }
49553     }
49554 });/*
49555  * Based on:
49556  * Ext JS Library 1.1.1
49557  * Copyright(c) 2006-2007, Ext JS, LLC.
49558  *
49559  * Originally Released Under LGPL - original licence link has changed is not relivant.
49560  *
49561  * Fork - LGPL
49562  * <script type="text/javascript">
49563  */
49564 /**
49565  * @class Roo.form.VTypes
49566  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49567  * @singleton
49568  */
49569 Roo.form.VTypes = function(){
49570     // closure these in so they are only created once.
49571     var alpha = /^[a-zA-Z_]+$/;
49572     var alphanum = /^[a-zA-Z0-9_]+$/;
49573     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49574     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49575
49576     // All these messages and functions are configurable
49577     return {
49578         /**
49579          * The function used to validate email addresses
49580          * @param {String} value The email address
49581          */
49582         'email' : function(v){
49583             return email.test(v);
49584         },
49585         /**
49586          * The error text to display when the email validation function returns false
49587          * @type String
49588          */
49589         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49590         /**
49591          * The keystroke filter mask to be applied on email input
49592          * @type RegExp
49593          */
49594         'emailMask' : /[a-z0-9_\.\-@]/i,
49595
49596         /**
49597          * The function used to validate URLs
49598          * @param {String} value The URL
49599          */
49600         'url' : function(v){
49601             return url.test(v);
49602         },
49603         /**
49604          * The error text to display when the url validation function returns false
49605          * @type String
49606          */
49607         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49608         
49609         /**
49610          * The function used to validate alpha values
49611          * @param {String} value The value
49612          */
49613         'alpha' : function(v){
49614             return alpha.test(v);
49615         },
49616         /**
49617          * The error text to display when the alpha validation function returns false
49618          * @type String
49619          */
49620         'alphaText' : 'This field should only contain letters and _',
49621         /**
49622          * The keystroke filter mask to be applied on alpha input
49623          * @type RegExp
49624          */
49625         'alphaMask' : /[a-z_]/i,
49626
49627         /**
49628          * The function used to validate alphanumeric values
49629          * @param {String} value The value
49630          */
49631         'alphanum' : function(v){
49632             return alphanum.test(v);
49633         },
49634         /**
49635          * The error text to display when the alphanumeric validation function returns false
49636          * @type String
49637          */
49638         'alphanumText' : 'This field should only contain letters, numbers and _',
49639         /**
49640          * The keystroke filter mask to be applied on alphanumeric input
49641          * @type RegExp
49642          */
49643         'alphanumMask' : /[a-z0-9_]/i
49644     };
49645 }();//<script type="text/javascript">
49646
49647 /**
49648  * @class Roo.form.FCKeditor
49649  * @extends Roo.form.TextArea
49650  * Wrapper around the FCKEditor http://www.fckeditor.net
49651  * @constructor
49652  * Creates a new FCKeditor
49653  * @param {Object} config Configuration options
49654  */
49655 Roo.form.FCKeditor = function(config){
49656     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49657     this.addEvents({
49658          /**
49659          * @event editorinit
49660          * Fired when the editor is initialized - you can add extra handlers here..
49661          * @param {FCKeditor} this
49662          * @param {Object} the FCK object.
49663          */
49664         editorinit : true
49665     });
49666     
49667     
49668 };
49669 Roo.form.FCKeditor.editors = { };
49670 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49671 {
49672     //defaultAutoCreate : {
49673     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49674     //},
49675     // private
49676     /**
49677      * @cfg {Object} fck options - see fck manual for details.
49678      */
49679     fckconfig : false,
49680     
49681     /**
49682      * @cfg {Object} fck toolbar set (Basic or Default)
49683      */
49684     toolbarSet : 'Basic',
49685     /**
49686      * @cfg {Object} fck BasePath
49687      */ 
49688     basePath : '/fckeditor/',
49689     
49690     
49691     frame : false,
49692     
49693     value : '',
49694     
49695    
49696     onRender : function(ct, position)
49697     {
49698         if(!this.el){
49699             this.defaultAutoCreate = {
49700                 tag: "textarea",
49701                 style:"width:300px;height:60px;",
49702                 autocomplete: "new-password"
49703             };
49704         }
49705         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49706         /*
49707         if(this.grow){
49708             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49709             if(this.preventScrollbars){
49710                 this.el.setStyle("overflow", "hidden");
49711             }
49712             this.el.setHeight(this.growMin);
49713         }
49714         */
49715         //console.log('onrender' + this.getId() );
49716         Roo.form.FCKeditor.editors[this.getId()] = this;
49717          
49718
49719         this.replaceTextarea() ;
49720         
49721     },
49722     
49723     getEditor : function() {
49724         return this.fckEditor;
49725     },
49726     /**
49727      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49728      * @param {Mixed} value The value to set
49729      */
49730     
49731     
49732     setValue : function(value)
49733     {
49734         //console.log('setValue: ' + value);
49735         
49736         if(typeof(value) == 'undefined') { // not sure why this is happending...
49737             return;
49738         }
49739         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49740         
49741         //if(!this.el || !this.getEditor()) {
49742         //    this.value = value;
49743             //this.setValue.defer(100,this,[value]);    
49744         //    return;
49745         //} 
49746         
49747         if(!this.getEditor()) {
49748             return;
49749         }
49750         
49751         this.getEditor().SetData(value);
49752         
49753         //
49754
49755     },
49756
49757     /**
49758      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49759      * @return {Mixed} value The field value
49760      */
49761     getValue : function()
49762     {
49763         
49764         if (this.frame && this.frame.dom.style.display == 'none') {
49765             return Roo.form.FCKeditor.superclass.getValue.call(this);
49766         }
49767         
49768         if(!this.el || !this.getEditor()) {
49769            
49770            // this.getValue.defer(100,this); 
49771             return this.value;
49772         }
49773        
49774         
49775         var value=this.getEditor().GetData();
49776         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49777         return Roo.form.FCKeditor.superclass.getValue.call(this);
49778         
49779
49780     },
49781
49782     /**
49783      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49784      * @return {Mixed} value The field value
49785      */
49786     getRawValue : function()
49787     {
49788         if (this.frame && this.frame.dom.style.display == 'none') {
49789             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49790         }
49791         
49792         if(!this.el || !this.getEditor()) {
49793             //this.getRawValue.defer(100,this); 
49794             return this.value;
49795             return;
49796         }
49797         
49798         
49799         
49800         var value=this.getEditor().GetData();
49801         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49802         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49803          
49804     },
49805     
49806     setSize : function(w,h) {
49807         
49808         
49809         
49810         //if (this.frame && this.frame.dom.style.display == 'none') {
49811         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49812         //    return;
49813         //}
49814         //if(!this.el || !this.getEditor()) {
49815         //    this.setSize.defer(100,this, [w,h]); 
49816         //    return;
49817         //}
49818         
49819         
49820         
49821         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49822         
49823         this.frame.dom.setAttribute('width', w);
49824         this.frame.dom.setAttribute('height', h);
49825         this.frame.setSize(w,h);
49826         
49827     },
49828     
49829     toggleSourceEdit : function(value) {
49830         
49831       
49832          
49833         this.el.dom.style.display = value ? '' : 'none';
49834         this.frame.dom.style.display = value ?  'none' : '';
49835         
49836     },
49837     
49838     
49839     focus: function(tag)
49840     {
49841         if (this.frame.dom.style.display == 'none') {
49842             return Roo.form.FCKeditor.superclass.focus.call(this);
49843         }
49844         if(!this.el || !this.getEditor()) {
49845             this.focus.defer(100,this, [tag]); 
49846             return;
49847         }
49848         
49849         
49850         
49851         
49852         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49853         this.getEditor().Focus();
49854         if (tgs.length) {
49855             if (!this.getEditor().Selection.GetSelection()) {
49856                 this.focus.defer(100,this, [tag]); 
49857                 return;
49858             }
49859             
49860             
49861             var r = this.getEditor().EditorDocument.createRange();
49862             r.setStart(tgs[0],0);
49863             r.setEnd(tgs[0],0);
49864             this.getEditor().Selection.GetSelection().removeAllRanges();
49865             this.getEditor().Selection.GetSelection().addRange(r);
49866             this.getEditor().Focus();
49867         }
49868         
49869     },
49870     
49871     
49872     
49873     replaceTextarea : function()
49874     {
49875         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49876             return ;
49877         }
49878         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49879         //{
49880             // We must check the elements firstly using the Id and then the name.
49881         var oTextarea = document.getElementById( this.getId() );
49882         
49883         var colElementsByName = document.getElementsByName( this.getId() ) ;
49884          
49885         oTextarea.style.display = 'none' ;
49886
49887         if ( oTextarea.tabIndex ) {            
49888             this.TabIndex = oTextarea.tabIndex ;
49889         }
49890         
49891         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49892         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49893         this.frame = Roo.get(this.getId() + '___Frame')
49894     },
49895     
49896     _getConfigHtml : function()
49897     {
49898         var sConfig = '' ;
49899
49900         for ( var o in this.fckconfig ) {
49901             sConfig += sConfig.length > 0  ? '&amp;' : '';
49902             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49903         }
49904
49905         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49906     },
49907     
49908     
49909     _getIFrameHtml : function()
49910     {
49911         var sFile = 'fckeditor.html' ;
49912         /* no idea what this is about..
49913         try
49914         {
49915             if ( (/fcksource=true/i).test( window.top.location.search ) )
49916                 sFile = 'fckeditor.original.html' ;
49917         }
49918         catch (e) { 
49919         */
49920
49921         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49922         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49923         
49924         
49925         var html = '<iframe id="' + this.getId() +
49926             '___Frame" src="' + sLink +
49927             '" width="' + this.width +
49928             '" height="' + this.height + '"' +
49929             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49930             ' frameborder="0" scrolling="no"></iframe>' ;
49931
49932         return html ;
49933     },
49934     
49935     _insertHtmlBefore : function( html, element )
49936     {
49937         if ( element.insertAdjacentHTML )       {
49938             // IE
49939             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49940         } else { // Gecko
49941             var oRange = document.createRange() ;
49942             oRange.setStartBefore( element ) ;
49943             var oFragment = oRange.createContextualFragment( html );
49944             element.parentNode.insertBefore( oFragment, element ) ;
49945         }
49946     }
49947     
49948     
49949   
49950     
49951     
49952     
49953     
49954
49955 });
49956
49957 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49958
49959 function FCKeditor_OnComplete(editorInstance){
49960     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49961     f.fckEditor = editorInstance;
49962     //console.log("loaded");
49963     f.fireEvent('editorinit', f, editorInstance);
49964
49965   
49966
49967  
49968
49969
49970
49971
49972
49973
49974
49975
49976
49977
49978
49979
49980
49981
49982
49983 //<script type="text/javascript">
49984 /**
49985  * @class Roo.form.GridField
49986  * @extends Roo.form.Field
49987  * Embed a grid (or editable grid into a form)
49988  * STATUS ALPHA
49989  * 
49990  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49991  * it needs 
49992  * xgrid.store = Roo.data.Store
49993  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49994  * xgrid.store.reader = Roo.data.JsonReader 
49995  * 
49996  * 
49997  * @constructor
49998  * Creates a new GridField
49999  * @param {Object} config Configuration options
50000  */
50001 Roo.form.GridField = function(config){
50002     Roo.form.GridField.superclass.constructor.call(this, config);
50003      
50004 };
50005
50006 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50007     /**
50008      * @cfg {Number} width  - used to restrict width of grid..
50009      */
50010     width : 100,
50011     /**
50012      * @cfg {Number} height - used to restrict height of grid..
50013      */
50014     height : 50,
50015      /**
50016      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50017          * 
50018          *}
50019      */
50020     xgrid : false, 
50021     /**
50022      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50023      * {tag: "input", type: "checkbox", autocomplete: "off"})
50024      */
50025    // defaultAutoCreate : { tag: 'div' },
50026     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50027     /**
50028      * @cfg {String} addTitle Text to include for adding a title.
50029      */
50030     addTitle : false,
50031     //
50032     onResize : function(){
50033         Roo.form.Field.superclass.onResize.apply(this, arguments);
50034     },
50035
50036     initEvents : function(){
50037         // Roo.form.Checkbox.superclass.initEvents.call(this);
50038         // has no events...
50039        
50040     },
50041
50042
50043     getResizeEl : function(){
50044         return this.wrap;
50045     },
50046
50047     getPositionEl : function(){
50048         return this.wrap;
50049     },
50050
50051     // private
50052     onRender : function(ct, position){
50053         
50054         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50055         var style = this.style;
50056         delete this.style;
50057         
50058         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50059         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50060         this.viewEl = this.wrap.createChild({ tag: 'div' });
50061         if (style) {
50062             this.viewEl.applyStyles(style);
50063         }
50064         if (this.width) {
50065             this.viewEl.setWidth(this.width);
50066         }
50067         if (this.height) {
50068             this.viewEl.setHeight(this.height);
50069         }
50070         //if(this.inputValue !== undefined){
50071         //this.setValue(this.value);
50072         
50073         
50074         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50075         
50076         
50077         this.grid.render();
50078         this.grid.getDataSource().on('remove', this.refreshValue, this);
50079         this.grid.getDataSource().on('update', this.refreshValue, this);
50080         this.grid.on('afteredit', this.refreshValue, this);
50081  
50082     },
50083      
50084     
50085     /**
50086      * Sets the value of the item. 
50087      * @param {String} either an object  or a string..
50088      */
50089     setValue : function(v){
50090         //this.value = v;
50091         v = v || []; // empty set..
50092         // this does not seem smart - it really only affects memoryproxy grids..
50093         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50094             var ds = this.grid.getDataSource();
50095             // assumes a json reader..
50096             var data = {}
50097             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50098             ds.loadData( data);
50099         }
50100         // clear selection so it does not get stale.
50101         if (this.grid.sm) { 
50102             this.grid.sm.clearSelections();
50103         }
50104         
50105         Roo.form.GridField.superclass.setValue.call(this, v);
50106         this.refreshValue();
50107         // should load data in the grid really....
50108     },
50109     
50110     // private
50111     refreshValue: function() {
50112          var val = [];
50113         this.grid.getDataSource().each(function(r) {
50114             val.push(r.data);
50115         });
50116         this.el.dom.value = Roo.encode(val);
50117     }
50118     
50119      
50120     
50121     
50122 });/*
50123  * Based on:
50124  * Ext JS Library 1.1.1
50125  * Copyright(c) 2006-2007, Ext JS, LLC.
50126  *
50127  * Originally Released Under LGPL - original licence link has changed is not relivant.
50128  *
50129  * Fork - LGPL
50130  * <script type="text/javascript">
50131  */
50132 /**
50133  * @class Roo.form.DisplayField
50134  * @extends Roo.form.Field
50135  * A generic Field to display non-editable data.
50136  * @cfg {Boolean} closable (true|false) default false
50137  * @constructor
50138  * Creates a new Display Field item.
50139  * @param {Object} config Configuration options
50140  */
50141 Roo.form.DisplayField = function(config){
50142     Roo.form.DisplayField.superclass.constructor.call(this, config);
50143     
50144     this.addEvents({
50145         /**
50146          * @event close
50147          * Fires after the click the close btn
50148              * @param {Roo.form.DisplayField} this
50149              */
50150         close : true
50151     });
50152 };
50153
50154 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50155     inputType:      'hidden',
50156     allowBlank:     true,
50157     readOnly:         true,
50158     
50159  
50160     /**
50161      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50162      */
50163     focusClass : undefined,
50164     /**
50165      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50166      */
50167     fieldClass: 'x-form-field',
50168     
50169      /**
50170      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50171      */
50172     valueRenderer: undefined,
50173     
50174     width: 100,
50175     /**
50176      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50177      * {tag: "input", type: "checkbox", autocomplete: "off"})
50178      */
50179      
50180  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50181  
50182     closable : false,
50183     
50184     onResize : function(){
50185         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50186         
50187     },
50188
50189     initEvents : function(){
50190         // Roo.form.Checkbox.superclass.initEvents.call(this);
50191         // has no events...
50192         
50193         if(this.closable){
50194             this.closeEl.on('click', this.onClose, this);
50195         }
50196        
50197     },
50198
50199
50200     getResizeEl : function(){
50201         return this.wrap;
50202     },
50203
50204     getPositionEl : function(){
50205         return this.wrap;
50206     },
50207
50208     // private
50209     onRender : function(ct, position){
50210         
50211         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50212         //if(this.inputValue !== undefined){
50213         this.wrap = this.el.wrap();
50214         
50215         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50216         
50217         if(this.closable){
50218             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50219         }
50220         
50221         if (this.bodyStyle) {
50222             this.viewEl.applyStyles(this.bodyStyle);
50223         }
50224         //this.viewEl.setStyle('padding', '2px');
50225         
50226         this.setValue(this.value);
50227         
50228     },
50229 /*
50230     // private
50231     initValue : Roo.emptyFn,
50232
50233   */
50234
50235         // private
50236     onClick : function(){
50237         
50238     },
50239
50240     /**
50241      * Sets the checked state of the checkbox.
50242      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50243      */
50244     setValue : function(v){
50245         this.value = v;
50246         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50247         // this might be called before we have a dom element..
50248         if (!this.viewEl) {
50249             return;
50250         }
50251         this.viewEl.dom.innerHTML = html;
50252         Roo.form.DisplayField.superclass.setValue.call(this, v);
50253
50254     },
50255     
50256     onClose : function(e)
50257     {
50258         e.preventDefault();
50259         
50260         this.fireEvent('close', this);
50261     }
50262 });/*
50263  * 
50264  * Licence- LGPL
50265  * 
50266  */
50267
50268 /**
50269  * @class Roo.form.DayPicker
50270  * @extends Roo.form.Field
50271  * A Day picker show [M] [T] [W] ....
50272  * @constructor
50273  * Creates a new Day Picker
50274  * @param {Object} config Configuration options
50275  */
50276 Roo.form.DayPicker= function(config){
50277     Roo.form.DayPicker.superclass.constructor.call(this, config);
50278      
50279 };
50280
50281 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50282     /**
50283      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50284      */
50285     focusClass : undefined,
50286     /**
50287      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50288      */
50289     fieldClass: "x-form-field",
50290    
50291     /**
50292      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50293      * {tag: "input", type: "checkbox", autocomplete: "off"})
50294      */
50295     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50296     
50297    
50298     actionMode : 'viewEl', 
50299     //
50300     // private
50301  
50302     inputType : 'hidden',
50303     
50304      
50305     inputElement: false, // real input element?
50306     basedOn: false, // ????
50307     
50308     isFormField: true, // not sure where this is needed!!!!
50309
50310     onResize : function(){
50311         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50312         if(!this.boxLabel){
50313             this.el.alignTo(this.wrap, 'c-c');
50314         }
50315     },
50316
50317     initEvents : function(){
50318         Roo.form.Checkbox.superclass.initEvents.call(this);
50319         this.el.on("click", this.onClick,  this);
50320         this.el.on("change", this.onClick,  this);
50321     },
50322
50323
50324     getResizeEl : function(){
50325         return this.wrap;
50326     },
50327
50328     getPositionEl : function(){
50329         return this.wrap;
50330     },
50331
50332     
50333     // private
50334     onRender : function(ct, position){
50335         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50336        
50337         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50338         
50339         var r1 = '<table><tr>';
50340         var r2 = '<tr class="x-form-daypick-icons">';
50341         for (var i=0; i < 7; i++) {
50342             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50343             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50344         }
50345         
50346         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50347         viewEl.select('img').on('click', this.onClick, this);
50348         this.viewEl = viewEl;   
50349         
50350         
50351         // this will not work on Chrome!!!
50352         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50353         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50354         
50355         
50356           
50357
50358     },
50359
50360     // private
50361     initValue : Roo.emptyFn,
50362
50363     /**
50364      * Returns the checked state of the checkbox.
50365      * @return {Boolean} True if checked, else false
50366      */
50367     getValue : function(){
50368         return this.el.dom.value;
50369         
50370     },
50371
50372         // private
50373     onClick : function(e){ 
50374         //this.setChecked(!this.checked);
50375         Roo.get(e.target).toggleClass('x-menu-item-checked');
50376         this.refreshValue();
50377         //if(this.el.dom.checked != this.checked){
50378         //    this.setValue(this.el.dom.checked);
50379        // }
50380     },
50381     
50382     // private
50383     refreshValue : function()
50384     {
50385         var val = '';
50386         this.viewEl.select('img',true).each(function(e,i,n)  {
50387             val += e.is(".x-menu-item-checked") ? String(n) : '';
50388         });
50389         this.setValue(val, true);
50390     },
50391
50392     /**
50393      * Sets the checked state of the checkbox.
50394      * On is always based on a string comparison between inputValue and the param.
50395      * @param {Boolean/String} value - the value to set 
50396      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50397      */
50398     setValue : function(v,suppressEvent){
50399         if (!this.el.dom) {
50400             return;
50401         }
50402         var old = this.el.dom.value ;
50403         this.el.dom.value = v;
50404         if (suppressEvent) {
50405             return ;
50406         }
50407          
50408         // update display..
50409         this.viewEl.select('img',true).each(function(e,i,n)  {
50410             
50411             var on = e.is(".x-menu-item-checked");
50412             var newv = v.indexOf(String(n)) > -1;
50413             if (on != newv) {
50414                 e.toggleClass('x-menu-item-checked');
50415             }
50416             
50417         });
50418         
50419         
50420         this.fireEvent('change', this, v, old);
50421         
50422         
50423     },
50424    
50425     // handle setting of hidden value by some other method!!?!?
50426     setFromHidden: function()
50427     {
50428         if(!this.el){
50429             return;
50430         }
50431         //console.log("SET FROM HIDDEN");
50432         //alert('setFrom hidden');
50433         this.setValue(this.el.dom.value);
50434     },
50435     
50436     onDestroy : function()
50437     {
50438         if(this.viewEl){
50439             Roo.get(this.viewEl).remove();
50440         }
50441          
50442         Roo.form.DayPicker.superclass.onDestroy.call(this);
50443     }
50444
50445 });/*
50446  * RooJS Library 1.1.1
50447  * Copyright(c) 2008-2011  Alan Knowles
50448  *
50449  * License - LGPL
50450  */
50451  
50452
50453 /**
50454  * @class Roo.form.ComboCheck
50455  * @extends Roo.form.ComboBox
50456  * A combobox for multiple select items.
50457  *
50458  * FIXME - could do with a reset button..
50459  * 
50460  * @constructor
50461  * Create a new ComboCheck
50462  * @param {Object} config Configuration options
50463  */
50464 Roo.form.ComboCheck = function(config){
50465     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50466     // should verify some data...
50467     // like
50468     // hiddenName = required..
50469     // displayField = required
50470     // valudField == required
50471     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50472     var _t = this;
50473     Roo.each(req, function(e) {
50474         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50475             throw "Roo.form.ComboCheck : missing value for: " + e;
50476         }
50477     });
50478     
50479     
50480 };
50481
50482 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50483      
50484      
50485     editable : false,
50486      
50487     selectedClass: 'x-menu-item-checked', 
50488     
50489     // private
50490     onRender : function(ct, position){
50491         var _t = this;
50492         
50493         
50494         
50495         if(!this.tpl){
50496             var cls = 'x-combo-list';
50497
50498             
50499             this.tpl =  new Roo.Template({
50500                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50501                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50502                    '<span>{' + this.displayField + '}</span>' +
50503                     '</div>' 
50504                 
50505             });
50506         }
50507  
50508         
50509         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50510         this.view.singleSelect = false;
50511         this.view.multiSelect = true;
50512         this.view.toggleSelect = true;
50513         this.pageTb.add(new Roo.Toolbar.Fill(), {
50514             
50515             text: 'Done',
50516             handler: function()
50517             {
50518                 _t.collapse();
50519             }
50520         });
50521     },
50522     
50523     onViewOver : function(e, t){
50524         // do nothing...
50525         return;
50526         
50527     },
50528     
50529     onViewClick : function(doFocus,index){
50530         return;
50531         
50532     },
50533     select: function () {
50534         //Roo.log("SELECT CALLED");
50535     },
50536      
50537     selectByValue : function(xv, scrollIntoView){
50538         var ar = this.getValueArray();
50539         var sels = [];
50540         
50541         Roo.each(ar, function(v) {
50542             if(v === undefined || v === null){
50543                 return;
50544             }
50545             var r = this.findRecord(this.valueField, v);
50546             if(r){
50547                 sels.push(this.store.indexOf(r))
50548                 
50549             }
50550         },this);
50551         this.view.select(sels);
50552         return false;
50553     },
50554     
50555     
50556     
50557     onSelect : function(record, index){
50558        // Roo.log("onselect Called");
50559        // this is only called by the clear button now..
50560         this.view.clearSelections();
50561         this.setValue('[]');
50562         if (this.value != this.valueBefore) {
50563             this.fireEvent('change', this, this.value, this.valueBefore);
50564             this.valueBefore = this.value;
50565         }
50566     },
50567     getValueArray : function()
50568     {
50569         var ar = [] ;
50570         
50571         try {
50572             //Roo.log(this.value);
50573             if (typeof(this.value) == 'undefined') {
50574                 return [];
50575             }
50576             var ar = Roo.decode(this.value);
50577             return  ar instanceof Array ? ar : []; //?? valid?
50578             
50579         } catch(e) {
50580             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50581             return [];
50582         }
50583          
50584     },
50585     expand : function ()
50586     {
50587         
50588         Roo.form.ComboCheck.superclass.expand.call(this);
50589         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50590         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50591         
50592
50593     },
50594     
50595     collapse : function(){
50596         Roo.form.ComboCheck.superclass.collapse.call(this);
50597         var sl = this.view.getSelectedIndexes();
50598         var st = this.store;
50599         var nv = [];
50600         var tv = [];
50601         var r;
50602         Roo.each(sl, function(i) {
50603             r = st.getAt(i);
50604             nv.push(r.get(this.valueField));
50605         },this);
50606         this.setValue(Roo.encode(nv));
50607         if (this.value != this.valueBefore) {
50608
50609             this.fireEvent('change', this, this.value, this.valueBefore);
50610             this.valueBefore = this.value;
50611         }
50612         
50613     },
50614     
50615     setValue : function(v){
50616         // Roo.log(v);
50617         this.value = v;
50618         
50619         var vals = this.getValueArray();
50620         var tv = [];
50621         Roo.each(vals, function(k) {
50622             var r = this.findRecord(this.valueField, k);
50623             if(r){
50624                 tv.push(r.data[this.displayField]);
50625             }else if(this.valueNotFoundText !== undefined){
50626                 tv.push( this.valueNotFoundText );
50627             }
50628         },this);
50629        // Roo.log(tv);
50630         
50631         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50632         this.hiddenField.value = v;
50633         this.value = v;
50634     }
50635     
50636 });/*
50637  * Based on:
50638  * Ext JS Library 1.1.1
50639  * Copyright(c) 2006-2007, Ext JS, LLC.
50640  *
50641  * Originally Released Under LGPL - original licence link has changed is not relivant.
50642  *
50643  * Fork - LGPL
50644  * <script type="text/javascript">
50645  */
50646  
50647 /**
50648  * @class Roo.form.Signature
50649  * @extends Roo.form.Field
50650  * Signature field.  
50651  * @constructor
50652  * 
50653  * @param {Object} config Configuration options
50654  */
50655
50656 Roo.form.Signature = function(config){
50657     Roo.form.Signature.superclass.constructor.call(this, config);
50658     
50659     this.addEvents({// not in used??
50660          /**
50661          * @event confirm
50662          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50663              * @param {Roo.form.Signature} combo This combo box
50664              */
50665         'confirm' : true,
50666         /**
50667          * @event reset
50668          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50669              * @param {Roo.form.ComboBox} combo This combo box
50670              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50671              */
50672         'reset' : true
50673     });
50674 };
50675
50676 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50677     /**
50678      * @cfg {Object} labels Label to use when rendering a form.
50679      * defaults to 
50680      * labels : { 
50681      *      clear : "Clear",
50682      *      confirm : "Confirm"
50683      *  }
50684      */
50685     labels : { 
50686         clear : "Clear",
50687         confirm : "Confirm"
50688     },
50689     /**
50690      * @cfg {Number} width The signature panel width (defaults to 300)
50691      */
50692     width: 300,
50693     /**
50694      * @cfg {Number} height The signature panel height (defaults to 100)
50695      */
50696     height : 100,
50697     /**
50698      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50699      */
50700     allowBlank : false,
50701     
50702     //private
50703     // {Object} signPanel The signature SVG panel element (defaults to {})
50704     signPanel : {},
50705     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50706     isMouseDown : false,
50707     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50708     isConfirmed : false,
50709     // {String} signatureTmp SVG mapping string (defaults to empty string)
50710     signatureTmp : '',
50711     
50712     
50713     defaultAutoCreate : { // modified by initCompnoent..
50714         tag: "input",
50715         type:"hidden"
50716     },
50717
50718     // private
50719     onRender : function(ct, position){
50720         
50721         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50722         
50723         this.wrap = this.el.wrap({
50724             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50725         });
50726         
50727         this.createToolbar(this);
50728         this.signPanel = this.wrap.createChild({
50729                 tag: 'div',
50730                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50731             }, this.el
50732         );
50733             
50734         this.svgID = Roo.id();
50735         this.svgEl = this.signPanel.createChild({
50736               xmlns : 'http://www.w3.org/2000/svg',
50737               tag : 'svg',
50738               id : this.svgID + "-svg",
50739               width: this.width,
50740               height: this.height,
50741               viewBox: '0 0 '+this.width+' '+this.height,
50742               cn : [
50743                 {
50744                     tag: "rect",
50745                     id: this.svgID + "-svg-r",
50746                     width: this.width,
50747                     height: this.height,
50748                     fill: "#ffa"
50749                 },
50750                 {
50751                     tag: "line",
50752                     id: this.svgID + "-svg-l",
50753                     x1: "0", // start
50754                     y1: (this.height*0.8), // start set the line in 80% of height
50755                     x2: this.width, // end
50756                     y2: (this.height*0.8), // end set the line in 80% of height
50757                     'stroke': "#666",
50758                     'stroke-width': "1",
50759                     'stroke-dasharray': "3",
50760                     'shape-rendering': "crispEdges",
50761                     'pointer-events': "none"
50762                 },
50763                 {
50764                     tag: "path",
50765                     id: this.svgID + "-svg-p",
50766                     'stroke': "navy",
50767                     'stroke-width': "3",
50768                     'fill': "none",
50769                     'pointer-events': 'none'
50770                 }
50771               ]
50772         });
50773         this.createSVG();
50774         this.svgBox = this.svgEl.dom.getScreenCTM();
50775     },
50776     createSVG : function(){ 
50777         var svg = this.signPanel;
50778         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50779         var t = this;
50780
50781         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50782         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50783         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50784         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50785         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50786         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50787         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50788         
50789     },
50790     isTouchEvent : function(e){
50791         return e.type.match(/^touch/);
50792     },
50793     getCoords : function (e) {
50794         var pt    = this.svgEl.dom.createSVGPoint();
50795         pt.x = e.clientX; 
50796         pt.y = e.clientY;
50797         if (this.isTouchEvent(e)) {
50798             pt.x =  e.targetTouches[0].clientX;
50799             pt.y = e.targetTouches[0].clientY;
50800         }
50801         var a = this.svgEl.dom.getScreenCTM();
50802         var b = a.inverse();
50803         var mx = pt.matrixTransform(b);
50804         return mx.x + ',' + mx.y;
50805     },
50806     //mouse event headler 
50807     down : function (e) {
50808         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50809         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50810         
50811         this.isMouseDown = true;
50812         
50813         e.preventDefault();
50814     },
50815     move : function (e) {
50816         if (this.isMouseDown) {
50817             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50818             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50819         }
50820         
50821         e.preventDefault();
50822     },
50823     up : function (e) {
50824         this.isMouseDown = false;
50825         var sp = this.signatureTmp.split(' ');
50826         
50827         if(sp.length > 1){
50828             if(!sp[sp.length-2].match(/^L/)){
50829                 sp.pop();
50830                 sp.pop();
50831                 sp.push("");
50832                 this.signatureTmp = sp.join(" ");
50833             }
50834         }
50835         if(this.getValue() != this.signatureTmp){
50836             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50837             this.isConfirmed = false;
50838         }
50839         e.preventDefault();
50840     },
50841     
50842     /**
50843      * Protected method that will not generally be called directly. It
50844      * is called when the editor creates its toolbar. Override this method if you need to
50845      * add custom toolbar buttons.
50846      * @param {HtmlEditor} editor
50847      */
50848     createToolbar : function(editor){
50849          function btn(id, toggle, handler){
50850             var xid = fid + '-'+ id ;
50851             return {
50852                 id : xid,
50853                 cmd : id,
50854                 cls : 'x-btn-icon x-edit-'+id,
50855                 enableToggle:toggle !== false,
50856                 scope: editor, // was editor...
50857                 handler:handler||editor.relayBtnCmd,
50858                 clickEvent:'mousedown',
50859                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50860                 tabIndex:-1
50861             };
50862         }
50863         
50864         
50865         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50866         this.tb = tb;
50867         this.tb.add(
50868            {
50869                 cls : ' x-signature-btn x-signature-'+id,
50870                 scope: editor, // was editor...
50871                 handler: this.reset,
50872                 clickEvent:'mousedown',
50873                 text: this.labels.clear
50874             },
50875             {
50876                  xtype : 'Fill',
50877                  xns: Roo.Toolbar
50878             }, 
50879             {
50880                 cls : '  x-signature-btn x-signature-'+id,
50881                 scope: editor, // was editor...
50882                 handler: this.confirmHandler,
50883                 clickEvent:'mousedown',
50884                 text: this.labels.confirm
50885             }
50886         );
50887     
50888     },
50889     //public
50890     /**
50891      * when user is clicked confirm then show this image.....
50892      * 
50893      * @return {String} Image Data URI
50894      */
50895     getImageDataURI : function(){
50896         var svg = this.svgEl.dom.parentNode.innerHTML;
50897         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50898         return src; 
50899     },
50900     /**
50901      * 
50902      * @return {Boolean} this.isConfirmed
50903      */
50904     getConfirmed : function(){
50905         return this.isConfirmed;
50906     },
50907     /**
50908      * 
50909      * @return {Number} this.width
50910      */
50911     getWidth : function(){
50912         return this.width;
50913     },
50914     /**
50915      * 
50916      * @return {Number} this.height
50917      */
50918     getHeight : function(){
50919         return this.height;
50920     },
50921     // private
50922     getSignature : function(){
50923         return this.signatureTmp;
50924     },
50925     // private
50926     reset : function(){
50927         this.signatureTmp = '';
50928         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50929         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50930         this.isConfirmed = false;
50931         Roo.form.Signature.superclass.reset.call(this);
50932     },
50933     setSignature : function(s){
50934         this.signatureTmp = s;
50935         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50936         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50937         this.setValue(s);
50938         this.isConfirmed = false;
50939         Roo.form.Signature.superclass.reset.call(this);
50940     }, 
50941     test : function(){
50942 //        Roo.log(this.signPanel.dom.contentWindow.up())
50943     },
50944     //private
50945     setConfirmed : function(){
50946         
50947         
50948         
50949 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50950     },
50951     // private
50952     confirmHandler : function(){
50953         if(!this.getSignature()){
50954             return;
50955         }
50956         
50957         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50958         this.setValue(this.getSignature());
50959         this.isConfirmed = true;
50960         
50961         this.fireEvent('confirm', this);
50962     },
50963     // private
50964     // Subclasses should provide the validation implementation by overriding this
50965     validateValue : function(value){
50966         if(this.allowBlank){
50967             return true;
50968         }
50969         
50970         if(this.isConfirmed){
50971             return true;
50972         }
50973         return false;
50974     }
50975 });/*
50976  * Based on:
50977  * Ext JS Library 1.1.1
50978  * Copyright(c) 2006-2007, Ext JS, LLC.
50979  *
50980  * Originally Released Under LGPL - original licence link has changed is not relivant.
50981  *
50982  * Fork - LGPL
50983  * <script type="text/javascript">
50984  */
50985  
50986
50987 /**
50988  * @class Roo.form.ComboBox
50989  * @extends Roo.form.TriggerField
50990  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50991  * @constructor
50992  * Create a new ComboBox.
50993  * @param {Object} config Configuration options
50994  */
50995 Roo.form.Select = function(config){
50996     Roo.form.Select.superclass.constructor.call(this, config);
50997      
50998 };
50999
51000 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51001     /**
51002      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51003      */
51004     /**
51005      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51006      * rendering into an Roo.Editor, defaults to false)
51007      */
51008     /**
51009      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51010      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51011      */
51012     /**
51013      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51014      */
51015     /**
51016      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51017      * the dropdown list (defaults to undefined, with no header element)
51018      */
51019
51020      /**
51021      * @cfg {String/Roo.Template} tpl The template to use to render the output
51022      */
51023      
51024     // private
51025     defaultAutoCreate : {tag: "select"  },
51026     /**
51027      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51028      */
51029     listWidth: undefined,
51030     /**
51031      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51032      * mode = 'remote' or 'text' if mode = 'local')
51033      */
51034     displayField: undefined,
51035     /**
51036      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51037      * mode = 'remote' or 'value' if mode = 'local'). 
51038      * Note: use of a valueField requires the user make a selection
51039      * in order for a value to be mapped.
51040      */
51041     valueField: undefined,
51042     
51043     
51044     /**
51045      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51046      * field's data value (defaults to the underlying DOM element's name)
51047      */
51048     hiddenName: undefined,
51049     /**
51050      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51051      */
51052     listClass: '',
51053     /**
51054      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51055      */
51056     selectedClass: 'x-combo-selected',
51057     /**
51058      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51059      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51060      * which displays a downward arrow icon).
51061      */
51062     triggerClass : 'x-form-arrow-trigger',
51063     /**
51064      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51065      */
51066     shadow:'sides',
51067     /**
51068      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51069      * anchor positions (defaults to 'tl-bl')
51070      */
51071     listAlign: 'tl-bl?',
51072     /**
51073      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51074      */
51075     maxHeight: 300,
51076     /**
51077      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51078      * query specified by the allQuery config option (defaults to 'query')
51079      */
51080     triggerAction: 'query',
51081     /**
51082      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51083      * (defaults to 4, does not apply if editable = false)
51084      */
51085     minChars : 4,
51086     /**
51087      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51088      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51089      */
51090     typeAhead: false,
51091     /**
51092      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51093      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51094      */
51095     queryDelay: 500,
51096     /**
51097      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51098      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51099      */
51100     pageSize: 0,
51101     /**
51102      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51103      * when editable = true (defaults to false)
51104      */
51105     selectOnFocus:false,
51106     /**
51107      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51108      */
51109     queryParam: 'query',
51110     /**
51111      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51112      * when mode = 'remote' (defaults to 'Loading...')
51113      */
51114     loadingText: 'Loading...',
51115     /**
51116      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51117      */
51118     resizable: false,
51119     /**
51120      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51121      */
51122     handleHeight : 8,
51123     /**
51124      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51125      * traditional select (defaults to true)
51126      */
51127     editable: true,
51128     /**
51129      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51130      */
51131     allQuery: '',
51132     /**
51133      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51134      */
51135     mode: 'remote',
51136     /**
51137      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51138      * listWidth has a higher value)
51139      */
51140     minListWidth : 70,
51141     /**
51142      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51143      * allow the user to set arbitrary text into the field (defaults to false)
51144      */
51145     forceSelection:false,
51146     /**
51147      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51148      * if typeAhead = true (defaults to 250)
51149      */
51150     typeAheadDelay : 250,
51151     /**
51152      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51153      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51154      */
51155     valueNotFoundText : undefined,
51156     
51157     /**
51158      * @cfg {String} defaultValue The value displayed after loading the store.
51159      */
51160     defaultValue: '',
51161     
51162     /**
51163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51164      */
51165     blockFocus : false,
51166     
51167     /**
51168      * @cfg {Boolean} disableClear Disable showing of clear button.
51169      */
51170     disableClear : false,
51171     /**
51172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51173      */
51174     alwaysQuery : false,
51175     
51176     //private
51177     addicon : false,
51178     editicon: false,
51179     
51180     // element that contains real text value.. (when hidden is used..)
51181      
51182     // private
51183     onRender : function(ct, position){
51184         Roo.form.Field.prototype.onRender.call(this, ct, position);
51185         
51186         if(this.store){
51187             this.store.on('beforeload', this.onBeforeLoad, this);
51188             this.store.on('load', this.onLoad, this);
51189             this.store.on('loadexception', this.onLoadException, this);
51190             this.store.load({});
51191         }
51192         
51193         
51194         
51195     },
51196
51197     // private
51198     initEvents : function(){
51199         //Roo.form.ComboBox.superclass.initEvents.call(this);
51200  
51201     },
51202
51203     onDestroy : function(){
51204        
51205         if(this.store){
51206             this.store.un('beforeload', this.onBeforeLoad, this);
51207             this.store.un('load', this.onLoad, this);
51208             this.store.un('loadexception', this.onLoadException, this);
51209         }
51210         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51211     },
51212
51213     // private
51214     fireKey : function(e){
51215         if(e.isNavKeyPress() && !this.list.isVisible()){
51216             this.fireEvent("specialkey", this, e);
51217         }
51218     },
51219
51220     // private
51221     onResize: function(w, h){
51222         
51223         return; 
51224     
51225         
51226     },
51227
51228     /**
51229      * Allow or prevent the user from directly editing the field text.  If false is passed,
51230      * the user will only be able to select from the items defined in the dropdown list.  This method
51231      * is the runtime equivalent of setting the 'editable' config option at config time.
51232      * @param {Boolean} value True to allow the user to directly edit the field text
51233      */
51234     setEditable : function(value){
51235          
51236     },
51237
51238     // private
51239     onBeforeLoad : function(){
51240         
51241         Roo.log("Select before load");
51242         return;
51243     
51244         this.innerList.update(this.loadingText ?
51245                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51246         //this.restrictHeight();
51247         this.selectedIndex = -1;
51248     },
51249
51250     // private
51251     onLoad : function(){
51252
51253     
51254         var dom = this.el.dom;
51255         dom.innerHTML = '';
51256          var od = dom.ownerDocument;
51257          
51258         if (this.emptyText) {
51259             var op = od.createElement('option');
51260             op.setAttribute('value', '');
51261             op.innerHTML = String.format('{0}', this.emptyText);
51262             dom.appendChild(op);
51263         }
51264         if(this.store.getCount() > 0){
51265            
51266             var vf = this.valueField;
51267             var df = this.displayField;
51268             this.store.data.each(function(r) {
51269                 // which colmsn to use... testing - cdoe / title..
51270                 var op = od.createElement('option');
51271                 op.setAttribute('value', r.data[vf]);
51272                 op.innerHTML = String.format('{0}', r.data[df]);
51273                 dom.appendChild(op);
51274             });
51275             if (typeof(this.defaultValue != 'undefined')) {
51276                 this.setValue(this.defaultValue);
51277             }
51278             
51279              
51280         }else{
51281             //this.onEmptyResults();
51282         }
51283         //this.el.focus();
51284     },
51285     // private
51286     onLoadException : function()
51287     {
51288         dom.innerHTML = '';
51289             
51290         Roo.log("Select on load exception");
51291         return;
51292     
51293         this.collapse();
51294         Roo.log(this.store.reader.jsonData);
51295         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51296             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51297         }
51298         
51299         
51300     },
51301     // private
51302     onTypeAhead : function(){
51303          
51304     },
51305
51306     // private
51307     onSelect : function(record, index){
51308         Roo.log('on select?');
51309         return;
51310         if(this.fireEvent('beforeselect', this, record, index) !== false){
51311             this.setFromData(index > -1 ? record.data : false);
51312             this.collapse();
51313             this.fireEvent('select', this, record, index);
51314         }
51315     },
51316
51317     /**
51318      * Returns the currently selected field value or empty string if no value is set.
51319      * @return {String} value The selected value
51320      */
51321     getValue : function(){
51322         var dom = this.el.dom;
51323         this.value = dom.options[dom.selectedIndex].value;
51324         return this.value;
51325         
51326     },
51327
51328     /**
51329      * Clears any text/value currently set in the field
51330      */
51331     clearValue : function(){
51332         this.value = '';
51333         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51334         
51335     },
51336
51337     /**
51338      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51339      * will be displayed in the field.  If the value does not match the data value of an existing item,
51340      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51341      * Otherwise the field will be blank (although the value will still be set).
51342      * @param {String} value The value to match
51343      */
51344     setValue : function(v){
51345         var d = this.el.dom;
51346         for (var i =0; i < d.options.length;i++) {
51347             if (v == d.options[i].value) {
51348                 d.selectedIndex = i;
51349                 this.value = v;
51350                 return;
51351             }
51352         }
51353         this.clearValue();
51354     },
51355     /**
51356      * @property {Object} the last set data for the element
51357      */
51358     
51359     lastData : false,
51360     /**
51361      * Sets the value of the field based on a object which is related to the record format for the store.
51362      * @param {Object} value the value to set as. or false on reset?
51363      */
51364     setFromData : function(o){
51365         Roo.log('setfrom data?');
51366          
51367         
51368         
51369     },
51370     // private
51371     reset : function(){
51372         this.clearValue();
51373     },
51374     // private
51375     findRecord : function(prop, value){
51376         
51377         return false;
51378     
51379         var record;
51380         if(this.store.getCount() > 0){
51381             this.store.each(function(r){
51382                 if(r.data[prop] == value){
51383                     record = r;
51384                     return false;
51385                 }
51386                 return true;
51387             });
51388         }
51389         return record;
51390     },
51391     
51392     getName: function()
51393     {
51394         // returns hidden if it's set..
51395         if (!this.rendered) {return ''};
51396         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51397         
51398     },
51399      
51400
51401     
51402
51403     // private
51404     onEmptyResults : function(){
51405         Roo.log('empty results');
51406         //this.collapse();
51407     },
51408
51409     /**
51410      * Returns true if the dropdown list is expanded, else false.
51411      */
51412     isExpanded : function(){
51413         return false;
51414     },
51415
51416     /**
51417      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51418      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51419      * @param {String} value The data value of the item to select
51420      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51421      * selected item if it is not currently in view (defaults to true)
51422      * @return {Boolean} True if the value matched an item in the list, else false
51423      */
51424     selectByValue : function(v, scrollIntoView){
51425         Roo.log('select By Value');
51426         return false;
51427     
51428         if(v !== undefined && v !== null){
51429             var r = this.findRecord(this.valueField || this.displayField, v);
51430             if(r){
51431                 this.select(this.store.indexOf(r), scrollIntoView);
51432                 return true;
51433             }
51434         }
51435         return false;
51436     },
51437
51438     /**
51439      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51440      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51441      * @param {Number} index The zero-based index of the list item to select
51442      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51443      * selected item if it is not currently in view (defaults to true)
51444      */
51445     select : function(index, scrollIntoView){
51446         Roo.log('select ');
51447         return  ;
51448         
51449         this.selectedIndex = index;
51450         this.view.select(index);
51451         if(scrollIntoView !== false){
51452             var el = this.view.getNode(index);
51453             if(el){
51454                 this.innerList.scrollChildIntoView(el, false);
51455             }
51456         }
51457     },
51458
51459       
51460
51461     // private
51462     validateBlur : function(){
51463         
51464         return;
51465         
51466     },
51467
51468     // private
51469     initQuery : function(){
51470         this.doQuery(this.getRawValue());
51471     },
51472
51473     // private
51474     doForce : function(){
51475         if(this.el.dom.value.length > 0){
51476             this.el.dom.value =
51477                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51478              
51479         }
51480     },
51481
51482     /**
51483      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51484      * query allowing the query action to be canceled if needed.
51485      * @param {String} query The SQL query to execute
51486      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51487      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51488      * saved in the current store (defaults to false)
51489      */
51490     doQuery : function(q, forceAll){
51491         
51492         Roo.log('doQuery?');
51493         if(q === undefined || q === null){
51494             q = '';
51495         }
51496         var qe = {
51497             query: q,
51498             forceAll: forceAll,
51499             combo: this,
51500             cancel:false
51501         };
51502         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51503             return false;
51504         }
51505         q = qe.query;
51506         forceAll = qe.forceAll;
51507         if(forceAll === true || (q.length >= this.minChars)){
51508             if(this.lastQuery != q || this.alwaysQuery){
51509                 this.lastQuery = q;
51510                 if(this.mode == 'local'){
51511                     this.selectedIndex = -1;
51512                     if(forceAll){
51513                         this.store.clearFilter();
51514                     }else{
51515                         this.store.filter(this.displayField, q);
51516                     }
51517                     this.onLoad();
51518                 }else{
51519                     this.store.baseParams[this.queryParam] = q;
51520                     this.store.load({
51521                         params: this.getParams(q)
51522                     });
51523                     this.expand();
51524                 }
51525             }else{
51526                 this.selectedIndex = -1;
51527                 this.onLoad();   
51528             }
51529         }
51530     },
51531
51532     // private
51533     getParams : function(q){
51534         var p = {};
51535         //p[this.queryParam] = q;
51536         if(this.pageSize){
51537             p.start = 0;
51538             p.limit = this.pageSize;
51539         }
51540         return p;
51541     },
51542
51543     /**
51544      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51545      */
51546     collapse : function(){
51547         
51548     },
51549
51550     // private
51551     collapseIf : function(e){
51552         
51553     },
51554
51555     /**
51556      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51557      */
51558     expand : function(){
51559         
51560     } ,
51561
51562     // private
51563      
51564
51565     /** 
51566     * @cfg {Boolean} grow 
51567     * @hide 
51568     */
51569     /** 
51570     * @cfg {Number} growMin 
51571     * @hide 
51572     */
51573     /** 
51574     * @cfg {Number} growMax 
51575     * @hide 
51576     */
51577     /**
51578      * @hide
51579      * @method autoSize
51580      */
51581     
51582     setWidth : function()
51583     {
51584         
51585     },
51586     getResizeEl : function(){
51587         return this.el;
51588     }
51589 });//<script type="text/javasscript">
51590  
51591
51592 /**
51593  * @class Roo.DDView
51594  * A DnD enabled version of Roo.View.
51595  * @param {Element/String} container The Element in which to create the View.
51596  * @param {String} tpl The template string used to create the markup for each element of the View
51597  * @param {Object} config The configuration properties. These include all the config options of
51598  * {@link Roo.View} plus some specific to this class.<br>
51599  * <p>
51600  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51601  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51602  * <p>
51603  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51604 .x-view-drag-insert-above {
51605         border-top:1px dotted #3366cc;
51606 }
51607 .x-view-drag-insert-below {
51608         border-bottom:1px dotted #3366cc;
51609 }
51610 </code></pre>
51611  * 
51612  */
51613  
51614 Roo.DDView = function(container, tpl, config) {
51615     Roo.DDView.superclass.constructor.apply(this, arguments);
51616     this.getEl().setStyle("outline", "0px none");
51617     this.getEl().unselectable();
51618     if (this.dragGroup) {
51619         this.setDraggable(this.dragGroup.split(","));
51620     }
51621     if (this.dropGroup) {
51622         this.setDroppable(this.dropGroup.split(","));
51623     }
51624     if (this.deletable) {
51625         this.setDeletable();
51626     }
51627     this.isDirtyFlag = false;
51628         this.addEvents({
51629                 "drop" : true
51630         });
51631 };
51632
51633 Roo.extend(Roo.DDView, Roo.View, {
51634 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51635 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51636 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51637 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51638
51639         isFormField: true,
51640
51641         reset: Roo.emptyFn,
51642         
51643         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51644
51645         validate: function() {
51646                 return true;
51647         },
51648         
51649         destroy: function() {
51650                 this.purgeListeners();
51651                 this.getEl.removeAllListeners();
51652                 this.getEl().remove();
51653                 if (this.dragZone) {
51654                         if (this.dragZone.destroy) {
51655                                 this.dragZone.destroy();
51656                         }
51657                 }
51658                 if (this.dropZone) {
51659                         if (this.dropZone.destroy) {
51660                                 this.dropZone.destroy();
51661                         }
51662                 }
51663         },
51664
51665 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51666         getName: function() {
51667                 return this.name;
51668         },
51669
51670 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51671         setValue: function(v) {
51672                 if (!this.store) {
51673                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51674                 }
51675                 var data = {};
51676                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51677                 this.store.proxy = new Roo.data.MemoryProxy(data);
51678                 this.store.load();
51679         },
51680
51681 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51682         getValue: function() {
51683                 var result = '(';
51684                 this.store.each(function(rec) {
51685                         result += rec.id + ',';
51686                 });
51687                 return result.substr(0, result.length - 1) + ')';
51688         },
51689         
51690         getIds: function() {
51691                 var i = 0, result = new Array(this.store.getCount());
51692                 this.store.each(function(rec) {
51693                         result[i++] = rec.id;
51694                 });
51695                 return result;
51696         },
51697         
51698         isDirty: function() {
51699                 return this.isDirtyFlag;
51700         },
51701
51702 /**
51703  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51704  *      whole Element becomes the target, and this causes the drop gesture to append.
51705  */
51706     getTargetFromEvent : function(e) {
51707                 var target = e.getTarget();
51708                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51709                 target = target.parentNode;
51710                 }
51711                 if (!target) {
51712                         target = this.el.dom.lastChild || this.el.dom;
51713                 }
51714                 return target;
51715     },
51716
51717 /**
51718  *      Create the drag data which consists of an object which has the property "ddel" as
51719  *      the drag proxy element. 
51720  */
51721     getDragData : function(e) {
51722         var target = this.findItemFromChild(e.getTarget());
51723                 if(target) {
51724                         this.handleSelection(e);
51725                         var selNodes = this.getSelectedNodes();
51726             var dragData = {
51727                 source: this,
51728                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51729                 nodes: selNodes,
51730                 records: []
51731                         };
51732                         var selectedIndices = this.getSelectedIndexes();
51733                         for (var i = 0; i < selectedIndices.length; i++) {
51734                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51735                         }
51736                         if (selNodes.length == 1) {
51737                                 dragData.ddel = target.cloneNode(true); // the div element
51738                         } else {
51739                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51740                                 div.className = 'multi-proxy';
51741                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51742                                         div.appendChild(selNodes[i].cloneNode(true));
51743                                 }
51744                                 dragData.ddel = div;
51745                         }
51746             //console.log(dragData)
51747             //console.log(dragData.ddel.innerHTML)
51748                         return dragData;
51749                 }
51750         //console.log('nodragData')
51751                 return false;
51752     },
51753     
51754 /**     Specify to which ddGroup items in this DDView may be dragged. */
51755     setDraggable: function(ddGroup) {
51756         if (ddGroup instanceof Array) {
51757                 Roo.each(ddGroup, this.setDraggable, this);
51758                 return;
51759         }
51760         if (this.dragZone) {
51761                 this.dragZone.addToGroup(ddGroup);
51762         } else {
51763                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51764                                 containerScroll: true,
51765                                 ddGroup: ddGroup 
51766
51767                         });
51768 //                      Draggability implies selection. DragZone's mousedown selects the element.
51769                         if (!this.multiSelect) { this.singleSelect = true; }
51770
51771 //                      Wire the DragZone's handlers up to methods in *this*
51772                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51773                 }
51774     },
51775
51776 /**     Specify from which ddGroup this DDView accepts drops. */
51777     setDroppable: function(ddGroup) {
51778         if (ddGroup instanceof Array) {
51779                 Roo.each(ddGroup, this.setDroppable, this);
51780                 return;
51781         }
51782         if (this.dropZone) {
51783                 this.dropZone.addToGroup(ddGroup);
51784         } else {
51785                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51786                                 containerScroll: true,
51787                                 ddGroup: ddGroup
51788                         });
51789
51790 //                      Wire the DropZone's handlers up to methods in *this*
51791                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51792                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51793                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51794                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51795                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51796                 }
51797     },
51798
51799 /**     Decide whether to drop above or below a View node. */
51800     getDropPoint : function(e, n, dd){
51801         if (n == this.el.dom) { return "above"; }
51802                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51803                 var c = t + (b - t) / 2;
51804                 var y = Roo.lib.Event.getPageY(e);
51805                 if(y <= c) {
51806                         return "above";
51807                 }else{
51808                         return "below";
51809                 }
51810     },
51811
51812     onNodeEnter : function(n, dd, e, data){
51813                 return false;
51814     },
51815     
51816     onNodeOver : function(n, dd, e, data){
51817                 var pt = this.getDropPoint(e, n, dd);
51818                 // set the insert point style on the target node
51819                 var dragElClass = this.dropNotAllowed;
51820                 if (pt) {
51821                         var targetElClass;
51822                         if (pt == "above"){
51823                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51824                                 targetElClass = "x-view-drag-insert-above";
51825                         } else {
51826                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51827                                 targetElClass = "x-view-drag-insert-below";
51828                         }
51829                         if (this.lastInsertClass != targetElClass){
51830                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51831                                 this.lastInsertClass = targetElClass;
51832                         }
51833                 }
51834                 return dragElClass;
51835         },
51836
51837     onNodeOut : function(n, dd, e, data){
51838                 this.removeDropIndicators(n);
51839     },
51840
51841     onNodeDrop : function(n, dd, e, data){
51842         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51843                 return false;
51844         }
51845         var pt = this.getDropPoint(e, n, dd);
51846                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51847                 if (pt == "below") { insertAt++; }
51848                 for (var i = 0; i < data.records.length; i++) {
51849                         var r = data.records[i];
51850                         var dup = this.store.getById(r.id);
51851                         if (dup && (dd != this.dragZone)) {
51852                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51853                         } else {
51854                                 if (data.copy) {
51855                                         this.store.insert(insertAt++, r.copy());
51856                                 } else {
51857                                         data.source.isDirtyFlag = true;
51858                                         r.store.remove(r);
51859                                         this.store.insert(insertAt++, r);
51860                                 }
51861                                 this.isDirtyFlag = true;
51862                         }
51863                 }
51864                 this.dragZone.cachedTarget = null;
51865                 return true;
51866     },
51867
51868     removeDropIndicators : function(n){
51869                 if(n){
51870                         Roo.fly(n).removeClass([
51871                                 "x-view-drag-insert-above",
51872                                 "x-view-drag-insert-below"]);
51873                         this.lastInsertClass = "_noclass";
51874                 }
51875     },
51876
51877 /**
51878  *      Utility method. Add a delete option to the DDView's context menu.
51879  *      @param {String} imageUrl The URL of the "delete" icon image.
51880  */
51881         setDeletable: function(imageUrl) {
51882                 if (!this.singleSelect && !this.multiSelect) {
51883                         this.singleSelect = true;
51884                 }
51885                 var c = this.getContextMenu();
51886                 this.contextMenu.on("itemclick", function(item) {
51887                         switch (item.id) {
51888                                 case "delete":
51889                                         this.remove(this.getSelectedIndexes());
51890                                         break;
51891                         }
51892                 }, this);
51893                 this.contextMenu.add({
51894                         icon: imageUrl,
51895                         id: "delete",
51896                         text: 'Delete'
51897                 });
51898         },
51899         
51900 /**     Return the context menu for this DDView. */
51901         getContextMenu: function() {
51902                 if (!this.contextMenu) {
51903 //                      Create the View's context menu
51904                         this.contextMenu = new Roo.menu.Menu({
51905                                 id: this.id + "-contextmenu"
51906                         });
51907                         this.el.on("contextmenu", this.showContextMenu, this);
51908                 }
51909                 return this.contextMenu;
51910         },
51911         
51912         disableContextMenu: function() {
51913                 if (this.contextMenu) {
51914                         this.el.un("contextmenu", this.showContextMenu, this);
51915                 }
51916         },
51917
51918         showContextMenu: function(e, item) {
51919         item = this.findItemFromChild(e.getTarget());
51920                 if (item) {
51921                         e.stopEvent();
51922                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51923                         this.contextMenu.showAt(e.getXY());
51924             }
51925     },
51926
51927 /**
51928  *      Remove {@link Roo.data.Record}s at the specified indices.
51929  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51930  */
51931     remove: function(selectedIndices) {
51932                 selectedIndices = [].concat(selectedIndices);
51933                 for (var i = 0; i < selectedIndices.length; i++) {
51934                         var rec = this.store.getAt(selectedIndices[i]);
51935                         this.store.remove(rec);
51936                 }
51937     },
51938
51939 /**
51940  *      Double click fires the event, but also, if this is draggable, and there is only one other
51941  *      related DropZone, it transfers the selected node.
51942  */
51943     onDblClick : function(e){
51944         var item = this.findItemFromChild(e.getTarget());
51945         if(item){
51946             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51947                 return false;
51948             }
51949             if (this.dragGroup) {
51950                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51951                     while (targets.indexOf(this.dropZone) > -1) {
51952                             targets.remove(this.dropZone);
51953                                 }
51954                     if (targets.length == 1) {
51955                                         this.dragZone.cachedTarget = null;
51956                         var el = Roo.get(targets[0].getEl());
51957                         var box = el.getBox(true);
51958                         targets[0].onNodeDrop(el.dom, {
51959                                 target: el.dom,
51960                                 xy: [box.x, box.y + box.height - 1]
51961                         }, null, this.getDragData(e));
51962                     }
51963                 }
51964         }
51965     },
51966     
51967     handleSelection: function(e) {
51968                 this.dragZone.cachedTarget = null;
51969         var item = this.findItemFromChild(e.getTarget());
51970         if (!item) {
51971                 this.clearSelections(true);
51972                 return;
51973         }
51974                 if (item && (this.multiSelect || this.singleSelect)){
51975                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51976                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51977                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51978                                 this.unselect(item);
51979                         } else {
51980                                 this.select(item, this.multiSelect && e.ctrlKey);
51981                                 this.lastSelection = item;
51982                         }
51983                 }
51984     },
51985
51986     onItemClick : function(item, index, e){
51987                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51988                         return false;
51989                 }
51990                 return true;
51991     },
51992
51993     unselect : function(nodeInfo, suppressEvent){
51994                 var node = this.getNode(nodeInfo);
51995                 if(node && this.isSelected(node)){
51996                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51997                                 Roo.fly(node).removeClass(this.selectedClass);
51998                                 this.selections.remove(node);
51999                                 if(!suppressEvent){
52000                                         this.fireEvent("selectionchange", this, this.selections);
52001                                 }
52002                         }
52003                 }
52004     }
52005 });
52006 /*
52007  * Based on:
52008  * Ext JS Library 1.1.1
52009  * Copyright(c) 2006-2007, Ext JS, LLC.
52010  *
52011  * Originally Released Under LGPL - original licence link has changed is not relivant.
52012  *
52013  * Fork - LGPL
52014  * <script type="text/javascript">
52015  */
52016  
52017 /**
52018  * @class Roo.LayoutManager
52019  * @extends Roo.util.Observable
52020  * Base class for layout managers.
52021  */
52022 Roo.LayoutManager = function(container, config){
52023     Roo.LayoutManager.superclass.constructor.call(this);
52024     this.el = Roo.get(container);
52025     // ie scrollbar fix
52026     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52027         document.body.scroll = "no";
52028     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52029         this.el.position('relative');
52030     }
52031     this.id = this.el.id;
52032     this.el.addClass("x-layout-container");
52033     /** false to disable window resize monitoring @type Boolean */
52034     this.monitorWindowResize = true;
52035     this.regions = {};
52036     this.addEvents({
52037         /**
52038          * @event layout
52039          * Fires when a layout is performed. 
52040          * @param {Roo.LayoutManager} this
52041          */
52042         "layout" : true,
52043         /**
52044          * @event regionresized
52045          * Fires when the user resizes a region. 
52046          * @param {Roo.LayoutRegion} region The resized region
52047          * @param {Number} newSize The new size (width for east/west, height for north/south)
52048          */
52049         "regionresized" : true,
52050         /**
52051          * @event regioncollapsed
52052          * Fires when a region is collapsed. 
52053          * @param {Roo.LayoutRegion} region The collapsed region
52054          */
52055         "regioncollapsed" : true,
52056         /**
52057          * @event regionexpanded
52058          * Fires when a region is expanded.  
52059          * @param {Roo.LayoutRegion} region The expanded region
52060          */
52061         "regionexpanded" : true
52062     });
52063     this.updating = false;
52064     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52065 };
52066
52067 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52068     /**
52069      * Returns true if this layout is currently being updated
52070      * @return {Boolean}
52071      */
52072     isUpdating : function(){
52073         return this.updating; 
52074     },
52075     
52076     /**
52077      * Suspend the LayoutManager from doing auto-layouts while
52078      * making multiple add or remove calls
52079      */
52080     beginUpdate : function(){
52081         this.updating = true;    
52082     },
52083     
52084     /**
52085      * Restore auto-layouts and optionally disable the manager from performing a layout
52086      * @param {Boolean} noLayout true to disable a layout update 
52087      */
52088     endUpdate : function(noLayout){
52089         this.updating = false;
52090         if(!noLayout){
52091             this.layout();
52092         }    
52093     },
52094     
52095     layout: function(){
52096         
52097     },
52098     
52099     onRegionResized : function(region, newSize){
52100         this.fireEvent("regionresized", region, newSize);
52101         this.layout();
52102     },
52103     
52104     onRegionCollapsed : function(region){
52105         this.fireEvent("regioncollapsed", region);
52106     },
52107     
52108     onRegionExpanded : function(region){
52109         this.fireEvent("regionexpanded", region);
52110     },
52111         
52112     /**
52113      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52114      * performs box-model adjustments.
52115      * @return {Object} The size as an object {width: (the width), height: (the height)}
52116      */
52117     getViewSize : function(){
52118         var size;
52119         if(this.el.dom != document.body){
52120             size = this.el.getSize();
52121         }else{
52122             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52123         }
52124         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52125         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52126         return size;
52127     },
52128     
52129     /**
52130      * Returns the Element this layout is bound to.
52131      * @return {Roo.Element}
52132      */
52133     getEl : function(){
52134         return this.el;
52135     },
52136     
52137     /**
52138      * Returns the specified region.
52139      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52140      * @return {Roo.LayoutRegion}
52141      */
52142     getRegion : function(target){
52143         return this.regions[target.toLowerCase()];
52144     },
52145     
52146     onWindowResize : function(){
52147         if(this.monitorWindowResize){
52148             this.layout();
52149         }
52150     }
52151 });/*
52152  * Based on:
52153  * Ext JS Library 1.1.1
52154  * Copyright(c) 2006-2007, Ext JS, LLC.
52155  *
52156  * Originally Released Under LGPL - original licence link has changed is not relivant.
52157  *
52158  * Fork - LGPL
52159  * <script type="text/javascript">
52160  */
52161 /**
52162  * @class Roo.BorderLayout
52163  * @extends Roo.LayoutManager
52164  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52165  * please see: <br><br>
52166  * <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>
52167  * <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>
52168  * Example:
52169  <pre><code>
52170  var layout = new Roo.BorderLayout(document.body, {
52171     north: {
52172         initialSize: 25,
52173         titlebar: false
52174     },
52175     west: {
52176         split:true,
52177         initialSize: 200,
52178         minSize: 175,
52179         maxSize: 400,
52180         titlebar: true,
52181         collapsible: true
52182     },
52183     east: {
52184         split:true,
52185         initialSize: 202,
52186         minSize: 175,
52187         maxSize: 400,
52188         titlebar: true,
52189         collapsible: true
52190     },
52191     south: {
52192         split:true,
52193         initialSize: 100,
52194         minSize: 100,
52195         maxSize: 200,
52196         titlebar: true,
52197         collapsible: true
52198     },
52199     center: {
52200         titlebar: true,
52201         autoScroll:true,
52202         resizeTabs: true,
52203         minTabWidth: 50,
52204         preferredTabWidth: 150
52205     }
52206 });
52207
52208 // shorthand
52209 var CP = Roo.ContentPanel;
52210
52211 layout.beginUpdate();
52212 layout.add("north", new CP("north", "North"));
52213 layout.add("south", new CP("south", {title: "South", closable: true}));
52214 layout.add("west", new CP("west", {title: "West"}));
52215 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52216 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52217 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52218 layout.getRegion("center").showPanel("center1");
52219 layout.endUpdate();
52220 </code></pre>
52221
52222 <b>The container the layout is rendered into can be either the body element or any other element.
52223 If it is not the body element, the container needs to either be an absolute positioned element,
52224 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52225 the container size if it is not the body element.</b>
52226
52227 * @constructor
52228 * Create a new BorderLayout
52229 * @param {String/HTMLElement/Element} container The container this layout is bound to
52230 * @param {Object} config Configuration options
52231  */
52232 Roo.BorderLayout = function(container, config){
52233     config = config || {};
52234     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52235     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52236     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52237         var target = this.factory.validRegions[i];
52238         if(config[target]){
52239             this.addRegion(target, config[target]);
52240         }
52241     }
52242 };
52243
52244 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52245     /**
52246      * Creates and adds a new region if it doesn't already exist.
52247      * @param {String} target The target region key (north, south, east, west or center).
52248      * @param {Object} config The regions config object
52249      * @return {BorderLayoutRegion} The new region
52250      */
52251     addRegion : function(target, config){
52252         if(!this.regions[target]){
52253             var r = this.factory.create(target, this, config);
52254             this.bindRegion(target, r);
52255         }
52256         return this.regions[target];
52257     },
52258
52259     // private (kinda)
52260     bindRegion : function(name, r){
52261         this.regions[name] = r;
52262         r.on("visibilitychange", this.layout, this);
52263         r.on("paneladded", this.layout, this);
52264         r.on("panelremoved", this.layout, this);
52265         r.on("invalidated", this.layout, this);
52266         r.on("resized", this.onRegionResized, this);
52267         r.on("collapsed", this.onRegionCollapsed, this);
52268         r.on("expanded", this.onRegionExpanded, this);
52269     },
52270
52271     /**
52272      * Performs a layout update.
52273      */
52274     layout : function(){
52275         if(this.updating) {
52276             return;
52277         }
52278         var size = this.getViewSize();
52279         var w = size.width;
52280         var h = size.height;
52281         var centerW = w;
52282         var centerH = h;
52283         var centerY = 0;
52284         var centerX = 0;
52285         //var x = 0, y = 0;
52286
52287         var rs = this.regions;
52288         var north = rs["north"];
52289         var south = rs["south"]; 
52290         var west = rs["west"];
52291         var east = rs["east"];
52292         var center = rs["center"];
52293         //if(this.hideOnLayout){ // not supported anymore
52294             //c.el.setStyle("display", "none");
52295         //}
52296         if(north && north.isVisible()){
52297             var b = north.getBox();
52298             var m = north.getMargins();
52299             b.width = w - (m.left+m.right);
52300             b.x = m.left;
52301             b.y = m.top;
52302             centerY = b.height + b.y + m.bottom;
52303             centerH -= centerY;
52304             north.updateBox(this.safeBox(b));
52305         }
52306         if(south && south.isVisible()){
52307             var b = south.getBox();
52308             var m = south.getMargins();
52309             b.width = w - (m.left+m.right);
52310             b.x = m.left;
52311             var totalHeight = (b.height + m.top + m.bottom);
52312             b.y = h - totalHeight + m.top;
52313             centerH -= totalHeight;
52314             south.updateBox(this.safeBox(b));
52315         }
52316         if(west && west.isVisible()){
52317             var b = west.getBox();
52318             var m = west.getMargins();
52319             b.height = centerH - (m.top+m.bottom);
52320             b.x = m.left;
52321             b.y = centerY + m.top;
52322             var totalWidth = (b.width + m.left + m.right);
52323             centerX += totalWidth;
52324             centerW -= totalWidth;
52325             west.updateBox(this.safeBox(b));
52326         }
52327         if(east && east.isVisible()){
52328             var b = east.getBox();
52329             var m = east.getMargins();
52330             b.height = centerH - (m.top+m.bottom);
52331             var totalWidth = (b.width + m.left + m.right);
52332             b.x = w - totalWidth + m.left;
52333             b.y = centerY + m.top;
52334             centerW -= totalWidth;
52335             east.updateBox(this.safeBox(b));
52336         }
52337         if(center){
52338             var m = center.getMargins();
52339             var centerBox = {
52340                 x: centerX + m.left,
52341                 y: centerY + m.top,
52342                 width: centerW - (m.left+m.right),
52343                 height: centerH - (m.top+m.bottom)
52344             };
52345             //if(this.hideOnLayout){
52346                 //center.el.setStyle("display", "block");
52347             //}
52348             center.updateBox(this.safeBox(centerBox));
52349         }
52350         this.el.repaint();
52351         this.fireEvent("layout", this);
52352     },
52353
52354     // private
52355     safeBox : function(box){
52356         box.width = Math.max(0, box.width);
52357         box.height = Math.max(0, box.height);
52358         return box;
52359     },
52360
52361     /**
52362      * Adds a ContentPanel (or subclass) to this layout.
52363      * @param {String} target The target region key (north, south, east, west or center).
52364      * @param {Roo.ContentPanel} panel The panel to add
52365      * @return {Roo.ContentPanel} The added panel
52366      */
52367     add : function(target, panel){
52368          
52369         target = target.toLowerCase();
52370         return this.regions[target].add(panel);
52371     },
52372
52373     /**
52374      * Remove a ContentPanel (or subclass) to this layout.
52375      * @param {String} target The target region key (north, south, east, west or center).
52376      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52377      * @return {Roo.ContentPanel} The removed panel
52378      */
52379     remove : function(target, panel){
52380         target = target.toLowerCase();
52381         return this.regions[target].remove(panel);
52382     },
52383
52384     /**
52385      * Searches all regions for a panel with the specified id
52386      * @param {String} panelId
52387      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52388      */
52389     findPanel : function(panelId){
52390         var rs = this.regions;
52391         for(var target in rs){
52392             if(typeof rs[target] != "function"){
52393                 var p = rs[target].getPanel(panelId);
52394                 if(p){
52395                     return p;
52396                 }
52397             }
52398         }
52399         return null;
52400     },
52401
52402     /**
52403      * Searches all regions for a panel with the specified id and activates (shows) it.
52404      * @param {String/ContentPanel} panelId The panels id or the panel itself
52405      * @return {Roo.ContentPanel} The shown panel or null
52406      */
52407     showPanel : function(panelId) {
52408       var rs = this.regions;
52409       for(var target in rs){
52410          var r = rs[target];
52411          if(typeof r != "function"){
52412             if(r.hasPanel(panelId)){
52413                return r.showPanel(panelId);
52414             }
52415          }
52416       }
52417       return null;
52418    },
52419
52420    /**
52421      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52422      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52423      */
52424     restoreState : function(provider){
52425         if(!provider){
52426             provider = Roo.state.Manager;
52427         }
52428         var sm = new Roo.LayoutStateManager();
52429         sm.init(this, provider);
52430     },
52431
52432     /**
52433      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52434      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52435      * a valid ContentPanel config object.  Example:
52436      * <pre><code>
52437 // Create the main layout
52438 var layout = new Roo.BorderLayout('main-ct', {
52439     west: {
52440         split:true,
52441         minSize: 175,
52442         titlebar: true
52443     },
52444     center: {
52445         title:'Components'
52446     }
52447 }, 'main-ct');
52448
52449 // Create and add multiple ContentPanels at once via configs
52450 layout.batchAdd({
52451    west: {
52452        id: 'source-files',
52453        autoCreate:true,
52454        title:'Ext Source Files',
52455        autoScroll:true,
52456        fitToFrame:true
52457    },
52458    center : {
52459        el: cview,
52460        autoScroll:true,
52461        fitToFrame:true,
52462        toolbar: tb,
52463        resizeEl:'cbody'
52464    }
52465 });
52466 </code></pre>
52467      * @param {Object} regions An object containing ContentPanel configs by region name
52468      */
52469     batchAdd : function(regions){
52470         this.beginUpdate();
52471         for(var rname in regions){
52472             var lr = this.regions[rname];
52473             if(lr){
52474                 this.addTypedPanels(lr, regions[rname]);
52475             }
52476         }
52477         this.endUpdate();
52478     },
52479
52480     // private
52481     addTypedPanels : function(lr, ps){
52482         if(typeof ps == 'string'){
52483             lr.add(new Roo.ContentPanel(ps));
52484         }
52485         else if(ps instanceof Array){
52486             for(var i =0, len = ps.length; i < len; i++){
52487                 this.addTypedPanels(lr, ps[i]);
52488             }
52489         }
52490         else if(!ps.events){ // raw config?
52491             var el = ps.el;
52492             delete ps.el; // prevent conflict
52493             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52494         }
52495         else {  // panel object assumed!
52496             lr.add(ps);
52497         }
52498     },
52499     /**
52500      * Adds a xtype elements to the layout.
52501      * <pre><code>
52502
52503 layout.addxtype({
52504        xtype : 'ContentPanel',
52505        region: 'west',
52506        items: [ .... ]
52507    }
52508 );
52509
52510 layout.addxtype({
52511         xtype : 'NestedLayoutPanel',
52512         region: 'west',
52513         layout: {
52514            center: { },
52515            west: { }   
52516         },
52517         items : [ ... list of content panels or nested layout panels.. ]
52518    }
52519 );
52520 </code></pre>
52521      * @param {Object} cfg Xtype definition of item to add.
52522      */
52523     addxtype : function(cfg)
52524     {
52525         // basically accepts a pannel...
52526         // can accept a layout region..!?!?
52527         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52528         
52529         if (!cfg.xtype.match(/Panel$/)) {
52530             return false;
52531         }
52532         var ret = false;
52533         
52534         if (typeof(cfg.region) == 'undefined') {
52535             Roo.log("Failed to add Panel, region was not set");
52536             Roo.log(cfg);
52537             return false;
52538         }
52539         var region = cfg.region;
52540         delete cfg.region;
52541         
52542           
52543         var xitems = [];
52544         if (cfg.items) {
52545             xitems = cfg.items;
52546             delete cfg.items;
52547         }
52548         var nb = false;
52549         
52550         switch(cfg.xtype) 
52551         {
52552             case 'ContentPanel':  // ContentPanel (el, cfg)
52553             case 'ScrollPanel':  // ContentPanel (el, cfg)
52554             case 'ViewPanel': 
52555                 if(cfg.autoCreate) {
52556                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52557                 } else {
52558                     var el = this.el.createChild();
52559                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52560                 }
52561                 
52562                 this.add(region, ret);
52563                 break;
52564             
52565             
52566             case 'TreePanel': // our new panel!
52567                 cfg.el = this.el.createChild();
52568                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52569                 this.add(region, ret);
52570                 break;
52571             
52572             case 'NestedLayoutPanel': 
52573                 // create a new Layout (which is  a Border Layout...
52574                 var el = this.el.createChild();
52575                 var clayout = cfg.layout;
52576                 delete cfg.layout;
52577                 clayout.items   = clayout.items  || [];
52578                 // replace this exitems with the clayout ones..
52579                 xitems = clayout.items;
52580                  
52581                 
52582                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52583                     cfg.background = false;
52584                 }
52585                 var layout = new Roo.BorderLayout(el, clayout);
52586                 
52587                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52588                 //console.log('adding nested layout panel '  + cfg.toSource());
52589                 this.add(region, ret);
52590                 nb = {}; /// find first...
52591                 break;
52592                 
52593             case 'GridPanel': 
52594             
52595                 // needs grid and region
52596                 
52597                 //var el = this.getRegion(region).el.createChild();
52598                 var el = this.el.createChild();
52599                 // create the grid first...
52600                 
52601                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52602                 delete cfg.grid;
52603                 if (region == 'center' && this.active ) {
52604                     cfg.background = false;
52605                 }
52606                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52607                 
52608                 this.add(region, ret);
52609                 if (cfg.background) {
52610                     ret.on('activate', function(gp) {
52611                         if (!gp.grid.rendered) {
52612                             gp.grid.render();
52613                         }
52614                     });
52615                 } else {
52616                     grid.render();
52617                 }
52618                 break;
52619            
52620            
52621            
52622                 
52623                 
52624                 
52625             default:
52626                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52627                     
52628                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52629                     this.add(region, ret);
52630                 } else {
52631                 
52632                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52633                     return null;
52634                 }
52635                 
52636              // GridPanel (grid, cfg)
52637             
52638         }
52639         this.beginUpdate();
52640         // add children..
52641         var region = '';
52642         var abn = {};
52643         Roo.each(xitems, function(i)  {
52644             region = nb && i.region ? i.region : false;
52645             
52646             var add = ret.addxtype(i);
52647            
52648             if (region) {
52649                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52650                 if (!i.background) {
52651                     abn[region] = nb[region] ;
52652                 }
52653             }
52654             
52655         });
52656         this.endUpdate();
52657
52658         // make the last non-background panel active..
52659         //if (nb) { Roo.log(abn); }
52660         if (nb) {
52661             
52662             for(var r in abn) {
52663                 region = this.getRegion(r);
52664                 if (region) {
52665                     // tried using nb[r], but it does not work..
52666                      
52667                     region.showPanel(abn[r]);
52668                    
52669                 }
52670             }
52671         }
52672         return ret;
52673         
52674     }
52675 });
52676
52677 /**
52678  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52679  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52680  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52681  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52682  * <pre><code>
52683 // shorthand
52684 var CP = Roo.ContentPanel;
52685
52686 var layout = Roo.BorderLayout.create({
52687     north: {
52688         initialSize: 25,
52689         titlebar: false,
52690         panels: [new CP("north", "North")]
52691     },
52692     west: {
52693         split:true,
52694         initialSize: 200,
52695         minSize: 175,
52696         maxSize: 400,
52697         titlebar: true,
52698         collapsible: true,
52699         panels: [new CP("west", {title: "West"})]
52700     },
52701     east: {
52702         split:true,
52703         initialSize: 202,
52704         minSize: 175,
52705         maxSize: 400,
52706         titlebar: true,
52707         collapsible: true,
52708         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52709     },
52710     south: {
52711         split:true,
52712         initialSize: 100,
52713         minSize: 100,
52714         maxSize: 200,
52715         titlebar: true,
52716         collapsible: true,
52717         panels: [new CP("south", {title: "South", closable: true})]
52718     },
52719     center: {
52720         titlebar: true,
52721         autoScroll:true,
52722         resizeTabs: true,
52723         minTabWidth: 50,
52724         preferredTabWidth: 150,
52725         panels: [
52726             new CP("center1", {title: "Close Me", closable: true}),
52727             new CP("center2", {title: "Center Panel", closable: false})
52728         ]
52729     }
52730 }, document.body);
52731
52732 layout.getRegion("center").showPanel("center1");
52733 </code></pre>
52734  * @param config
52735  * @param targetEl
52736  */
52737 Roo.BorderLayout.create = function(config, targetEl){
52738     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52739     layout.beginUpdate();
52740     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52741     for(var j = 0, jlen = regions.length; j < jlen; j++){
52742         var lr = regions[j];
52743         if(layout.regions[lr] && config[lr].panels){
52744             var r = layout.regions[lr];
52745             var ps = config[lr].panels;
52746             layout.addTypedPanels(r, ps);
52747         }
52748     }
52749     layout.endUpdate();
52750     return layout;
52751 };
52752
52753 // private
52754 Roo.BorderLayout.RegionFactory = {
52755     // private
52756     validRegions : ["north","south","east","west","center"],
52757
52758     // private
52759     create : function(target, mgr, config){
52760         target = target.toLowerCase();
52761         if(config.lightweight || config.basic){
52762             return new Roo.BasicLayoutRegion(mgr, config, target);
52763         }
52764         switch(target){
52765             case "north":
52766                 return new Roo.NorthLayoutRegion(mgr, config);
52767             case "south":
52768                 return new Roo.SouthLayoutRegion(mgr, config);
52769             case "east":
52770                 return new Roo.EastLayoutRegion(mgr, config);
52771             case "west":
52772                 return new Roo.WestLayoutRegion(mgr, config);
52773             case "center":
52774                 return new Roo.CenterLayoutRegion(mgr, config);
52775         }
52776         throw 'Layout region "'+target+'" not supported.';
52777     }
52778 };/*
52779  * Based on:
52780  * Ext JS Library 1.1.1
52781  * Copyright(c) 2006-2007, Ext JS, LLC.
52782  *
52783  * Originally Released Under LGPL - original licence link has changed is not relivant.
52784  *
52785  * Fork - LGPL
52786  * <script type="text/javascript">
52787  */
52788  
52789 /**
52790  * @class Roo.BasicLayoutRegion
52791  * @extends Roo.util.Observable
52792  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52793  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52794  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52795  */
52796 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52797     this.mgr = mgr;
52798     this.position  = pos;
52799     this.events = {
52800         /**
52801          * @scope Roo.BasicLayoutRegion
52802          */
52803         
52804         /**
52805          * @event beforeremove
52806          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52807          * @param {Roo.LayoutRegion} this
52808          * @param {Roo.ContentPanel} panel The panel
52809          * @param {Object} e The cancel event object
52810          */
52811         "beforeremove" : true,
52812         /**
52813          * @event invalidated
52814          * Fires when the layout for this region is changed.
52815          * @param {Roo.LayoutRegion} this
52816          */
52817         "invalidated" : true,
52818         /**
52819          * @event visibilitychange
52820          * Fires when this region is shown or hidden 
52821          * @param {Roo.LayoutRegion} this
52822          * @param {Boolean} visibility true or false
52823          */
52824         "visibilitychange" : true,
52825         /**
52826          * @event paneladded
52827          * Fires when a panel is added. 
52828          * @param {Roo.LayoutRegion} this
52829          * @param {Roo.ContentPanel} panel The panel
52830          */
52831         "paneladded" : true,
52832         /**
52833          * @event panelremoved
52834          * Fires when a panel is removed. 
52835          * @param {Roo.LayoutRegion} this
52836          * @param {Roo.ContentPanel} panel The panel
52837          */
52838         "panelremoved" : true,
52839         /**
52840          * @event beforecollapse
52841          * Fires when this region before collapse.
52842          * @param {Roo.LayoutRegion} this
52843          */
52844         "beforecollapse" : true,
52845         /**
52846          * @event collapsed
52847          * Fires when this region is collapsed.
52848          * @param {Roo.LayoutRegion} this
52849          */
52850         "collapsed" : true,
52851         /**
52852          * @event expanded
52853          * Fires when this region is expanded.
52854          * @param {Roo.LayoutRegion} this
52855          */
52856         "expanded" : true,
52857         /**
52858          * @event slideshow
52859          * Fires when this region is slid into view.
52860          * @param {Roo.LayoutRegion} this
52861          */
52862         "slideshow" : true,
52863         /**
52864          * @event slidehide
52865          * Fires when this region slides out of view. 
52866          * @param {Roo.LayoutRegion} this
52867          */
52868         "slidehide" : true,
52869         /**
52870          * @event panelactivated
52871          * Fires when a panel is activated. 
52872          * @param {Roo.LayoutRegion} this
52873          * @param {Roo.ContentPanel} panel The activated panel
52874          */
52875         "panelactivated" : true,
52876         /**
52877          * @event resized
52878          * Fires when the user resizes this region. 
52879          * @param {Roo.LayoutRegion} this
52880          * @param {Number} newSize The new size (width for east/west, height for north/south)
52881          */
52882         "resized" : true
52883     };
52884     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52885     this.panels = new Roo.util.MixedCollection();
52886     this.panels.getKey = this.getPanelId.createDelegate(this);
52887     this.box = null;
52888     this.activePanel = null;
52889     // ensure listeners are added...
52890     
52891     if (config.listeners || config.events) {
52892         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52893             listeners : config.listeners || {},
52894             events : config.events || {}
52895         });
52896     }
52897     
52898     if(skipConfig !== true){
52899         this.applyConfig(config);
52900     }
52901 };
52902
52903 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52904     getPanelId : function(p){
52905         return p.getId();
52906     },
52907     
52908     applyConfig : function(config){
52909         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52910         this.config = config;
52911         
52912     },
52913     
52914     /**
52915      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52916      * the width, for horizontal (north, south) the height.
52917      * @param {Number} newSize The new width or height
52918      */
52919     resizeTo : function(newSize){
52920         var el = this.el ? this.el :
52921                  (this.activePanel ? this.activePanel.getEl() : null);
52922         if(el){
52923             switch(this.position){
52924                 case "east":
52925                 case "west":
52926                     el.setWidth(newSize);
52927                     this.fireEvent("resized", this, newSize);
52928                 break;
52929                 case "north":
52930                 case "south":
52931                     el.setHeight(newSize);
52932                     this.fireEvent("resized", this, newSize);
52933                 break;                
52934             }
52935         }
52936     },
52937     
52938     getBox : function(){
52939         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52940     },
52941     
52942     getMargins : function(){
52943         return this.margins;
52944     },
52945     
52946     updateBox : function(box){
52947         this.box = box;
52948         var el = this.activePanel.getEl();
52949         el.dom.style.left = box.x + "px";
52950         el.dom.style.top = box.y + "px";
52951         this.activePanel.setSize(box.width, box.height);
52952     },
52953     
52954     /**
52955      * Returns the container element for this region.
52956      * @return {Roo.Element}
52957      */
52958     getEl : function(){
52959         return this.activePanel;
52960     },
52961     
52962     /**
52963      * Returns true if this region is currently visible.
52964      * @return {Boolean}
52965      */
52966     isVisible : function(){
52967         return this.activePanel ? true : false;
52968     },
52969     
52970     setActivePanel : function(panel){
52971         panel = this.getPanel(panel);
52972         if(this.activePanel && this.activePanel != panel){
52973             this.activePanel.setActiveState(false);
52974             this.activePanel.getEl().setLeftTop(-10000,-10000);
52975         }
52976         this.activePanel = panel;
52977         panel.setActiveState(true);
52978         if(this.box){
52979             panel.setSize(this.box.width, this.box.height);
52980         }
52981         this.fireEvent("panelactivated", this, panel);
52982         this.fireEvent("invalidated");
52983     },
52984     
52985     /**
52986      * Show the specified panel.
52987      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52988      * @return {Roo.ContentPanel} The shown panel or null
52989      */
52990     showPanel : function(panel){
52991         if(panel = this.getPanel(panel)){
52992             this.setActivePanel(panel);
52993         }
52994         return panel;
52995     },
52996     
52997     /**
52998      * Get the active panel for this region.
52999      * @return {Roo.ContentPanel} The active panel or null
53000      */
53001     getActivePanel : function(){
53002         return this.activePanel;
53003     },
53004     
53005     /**
53006      * Add the passed ContentPanel(s)
53007      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53008      * @return {Roo.ContentPanel} The panel added (if only one was added)
53009      */
53010     add : function(panel){
53011         if(arguments.length > 1){
53012             for(var i = 0, len = arguments.length; i < len; i++) {
53013                 this.add(arguments[i]);
53014             }
53015             return null;
53016         }
53017         if(this.hasPanel(panel)){
53018             this.showPanel(panel);
53019             return panel;
53020         }
53021         var el = panel.getEl();
53022         if(el.dom.parentNode != this.mgr.el.dom){
53023             this.mgr.el.dom.appendChild(el.dom);
53024         }
53025         if(panel.setRegion){
53026             panel.setRegion(this);
53027         }
53028         this.panels.add(panel);
53029         el.setStyle("position", "absolute");
53030         if(!panel.background){
53031             this.setActivePanel(panel);
53032             if(this.config.initialSize && this.panels.getCount()==1){
53033                 this.resizeTo(this.config.initialSize);
53034             }
53035         }
53036         this.fireEvent("paneladded", this, panel);
53037         return panel;
53038     },
53039     
53040     /**
53041      * Returns true if the panel is in this region.
53042      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53043      * @return {Boolean}
53044      */
53045     hasPanel : function(panel){
53046         if(typeof panel == "object"){ // must be panel obj
53047             panel = panel.getId();
53048         }
53049         return this.getPanel(panel) ? true : false;
53050     },
53051     
53052     /**
53053      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53055      * @param {Boolean} preservePanel Overrides the config preservePanel option
53056      * @return {Roo.ContentPanel} The panel that was removed
53057      */
53058     remove : function(panel, preservePanel){
53059         panel = this.getPanel(panel);
53060         if(!panel){
53061             return null;
53062         }
53063         var e = {};
53064         this.fireEvent("beforeremove", this, panel, e);
53065         if(e.cancel === true){
53066             return null;
53067         }
53068         var panelId = panel.getId();
53069         this.panels.removeKey(panelId);
53070         return panel;
53071     },
53072     
53073     /**
53074      * Returns the panel specified or null if it's not in this region.
53075      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53076      * @return {Roo.ContentPanel}
53077      */
53078     getPanel : function(id){
53079         if(typeof id == "object"){ // must be panel obj
53080             return id;
53081         }
53082         return this.panels.get(id);
53083     },
53084     
53085     /**
53086      * Returns this regions position (north/south/east/west/center).
53087      * @return {String} 
53088      */
53089     getPosition: function(){
53090         return this.position;    
53091     }
53092 });/*
53093  * Based on:
53094  * Ext JS Library 1.1.1
53095  * Copyright(c) 2006-2007, Ext JS, LLC.
53096  *
53097  * Originally Released Under LGPL - original licence link has changed is not relivant.
53098  *
53099  * Fork - LGPL
53100  * <script type="text/javascript">
53101  */
53102  
53103 /**
53104  * @class Roo.LayoutRegion
53105  * @extends Roo.BasicLayoutRegion
53106  * This class represents a region in a layout manager.
53107  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53108  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53109  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53110  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53111  * @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})
53112  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53113  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53114  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53115  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53116  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53117  * @cfg {String}    title           The title for the region (overrides panel titles)
53118  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53119  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53120  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53121  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53122  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53123  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53124  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53125  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53126  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53127  * @cfg {Boolean}   showPin         True to show a pin button
53128  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53129  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53130  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53131  * @cfg {Number}    width           For East/West panels
53132  * @cfg {Number}    height          For North/South panels
53133  * @cfg {Boolean}   split           To show the splitter
53134  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53135  */
53136 Roo.LayoutRegion = function(mgr, config, pos){
53137     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53138     var dh = Roo.DomHelper;
53139     /** This region's container element 
53140     * @type Roo.Element */
53141     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53142     /** This region's title element 
53143     * @type Roo.Element */
53144
53145     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53146         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53147         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53148     ]}, true);
53149     this.titleEl.enableDisplayMode();
53150     /** This region's title text element 
53151     * @type HTMLElement */
53152     this.titleTextEl = this.titleEl.dom.firstChild;
53153     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53154     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53155     this.closeBtn.enableDisplayMode();
53156     this.closeBtn.on("click", this.closeClicked, this);
53157     this.closeBtn.hide();
53158
53159     this.createBody(config);
53160     this.visible = true;
53161     this.collapsed = false;
53162
53163     if(config.hideWhenEmpty){
53164         this.hide();
53165         this.on("paneladded", this.validateVisibility, this);
53166         this.on("panelremoved", this.validateVisibility, this);
53167     }
53168     this.applyConfig(config);
53169 };
53170
53171 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53172
53173     createBody : function(){
53174         /** This region's body element 
53175         * @type Roo.Element */
53176         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53177     },
53178
53179     applyConfig : function(c){
53180         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53181             var dh = Roo.DomHelper;
53182             if(c.titlebar !== false){
53183                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53184                 this.collapseBtn.on("click", this.collapse, this);
53185                 this.collapseBtn.enableDisplayMode();
53186
53187                 if(c.showPin === true || this.showPin){
53188                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53189                     this.stickBtn.enableDisplayMode();
53190                     this.stickBtn.on("click", this.expand, this);
53191                     this.stickBtn.hide();
53192                 }
53193             }
53194             /** This region's collapsed element
53195             * @type Roo.Element */
53196             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53197                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53198             ]}, true);
53199             if(c.floatable !== false){
53200                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53201                this.collapsedEl.on("click", this.collapseClick, this);
53202             }
53203
53204             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53205                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53206                    id: "message", unselectable: "on", style:{"float":"left"}});
53207                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53208              }
53209             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53210             this.expandBtn.on("click", this.expand, this);
53211         }
53212         if(this.collapseBtn){
53213             this.collapseBtn.setVisible(c.collapsible == true);
53214         }
53215         this.cmargins = c.cmargins || this.cmargins ||
53216                          (this.position == "west" || this.position == "east" ?
53217                              {top: 0, left: 2, right:2, bottom: 0} :
53218                              {top: 2, left: 0, right:0, bottom: 2});
53219         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53220         this.bottomTabs = c.tabPosition != "top";
53221         this.autoScroll = c.autoScroll || false;
53222         if(this.autoScroll){
53223             this.bodyEl.setStyle("overflow", "auto");
53224         }else{
53225             this.bodyEl.setStyle("overflow", "hidden");
53226         }
53227         //if(c.titlebar !== false){
53228             if((!c.titlebar && !c.title) || c.titlebar === false){
53229                 this.titleEl.hide();
53230             }else{
53231                 this.titleEl.show();
53232                 if(c.title){
53233                     this.titleTextEl.innerHTML = c.title;
53234                 }
53235             }
53236         //}
53237         this.duration = c.duration || .30;
53238         this.slideDuration = c.slideDuration || .45;
53239         this.config = c;
53240         if(c.collapsed){
53241             this.collapse(true);
53242         }
53243         if(c.hidden){
53244             this.hide();
53245         }
53246     },
53247     /**
53248      * Returns true if this region is currently visible.
53249      * @return {Boolean}
53250      */
53251     isVisible : function(){
53252         return this.visible;
53253     },
53254
53255     /**
53256      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53257      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53258      */
53259     setCollapsedTitle : function(title){
53260         title = title || "&#160;";
53261         if(this.collapsedTitleTextEl){
53262             this.collapsedTitleTextEl.innerHTML = title;
53263         }
53264     },
53265
53266     getBox : function(){
53267         var b;
53268         if(!this.collapsed){
53269             b = this.el.getBox(false, true);
53270         }else{
53271             b = this.collapsedEl.getBox(false, true);
53272         }
53273         return b;
53274     },
53275
53276     getMargins : function(){
53277         return this.collapsed ? this.cmargins : this.margins;
53278     },
53279
53280     highlight : function(){
53281         this.el.addClass("x-layout-panel-dragover");
53282     },
53283
53284     unhighlight : function(){
53285         this.el.removeClass("x-layout-panel-dragover");
53286     },
53287
53288     updateBox : function(box){
53289         this.box = box;
53290         if(!this.collapsed){
53291             this.el.dom.style.left = box.x + "px";
53292             this.el.dom.style.top = box.y + "px";
53293             this.updateBody(box.width, box.height);
53294         }else{
53295             this.collapsedEl.dom.style.left = box.x + "px";
53296             this.collapsedEl.dom.style.top = box.y + "px";
53297             this.collapsedEl.setSize(box.width, box.height);
53298         }
53299         if(this.tabs){
53300             this.tabs.autoSizeTabs();
53301         }
53302     },
53303
53304     updateBody : function(w, h){
53305         if(w !== null){
53306             this.el.setWidth(w);
53307             w -= this.el.getBorderWidth("rl");
53308             if(this.config.adjustments){
53309                 w += this.config.adjustments[0];
53310             }
53311         }
53312         if(h !== null){
53313             this.el.setHeight(h);
53314             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53315             h -= this.el.getBorderWidth("tb");
53316             if(this.config.adjustments){
53317                 h += this.config.adjustments[1];
53318             }
53319             this.bodyEl.setHeight(h);
53320             if(this.tabs){
53321                 h = this.tabs.syncHeight(h);
53322             }
53323         }
53324         if(this.panelSize){
53325             w = w !== null ? w : this.panelSize.width;
53326             h = h !== null ? h : this.panelSize.height;
53327         }
53328         if(this.activePanel){
53329             var el = this.activePanel.getEl();
53330             w = w !== null ? w : el.getWidth();
53331             h = h !== null ? h : el.getHeight();
53332             this.panelSize = {width: w, height: h};
53333             this.activePanel.setSize(w, h);
53334         }
53335         if(Roo.isIE && this.tabs){
53336             this.tabs.el.repaint();
53337         }
53338     },
53339
53340     /**
53341      * Returns the container element for this region.
53342      * @return {Roo.Element}
53343      */
53344     getEl : function(){
53345         return this.el;
53346     },
53347
53348     /**
53349      * Hides this region.
53350      */
53351     hide : function(){
53352         if(!this.collapsed){
53353             this.el.dom.style.left = "-2000px";
53354             this.el.hide();
53355         }else{
53356             this.collapsedEl.dom.style.left = "-2000px";
53357             this.collapsedEl.hide();
53358         }
53359         this.visible = false;
53360         this.fireEvent("visibilitychange", this, false);
53361     },
53362
53363     /**
53364      * Shows this region if it was previously hidden.
53365      */
53366     show : function(){
53367         if(!this.collapsed){
53368             this.el.show();
53369         }else{
53370             this.collapsedEl.show();
53371         }
53372         this.visible = true;
53373         this.fireEvent("visibilitychange", this, true);
53374     },
53375
53376     closeClicked : function(){
53377         if(this.activePanel){
53378             this.remove(this.activePanel);
53379         }
53380     },
53381
53382     collapseClick : function(e){
53383         if(this.isSlid){
53384            e.stopPropagation();
53385            this.slideIn();
53386         }else{
53387            e.stopPropagation();
53388            this.slideOut();
53389         }
53390     },
53391
53392     /**
53393      * Collapses this region.
53394      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53395      */
53396     collapse : function(skipAnim, skipCheck){
53397         if(this.collapsed) {
53398             return;
53399         }
53400         
53401         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53402             
53403             this.collapsed = true;
53404             if(this.split){
53405                 this.split.el.hide();
53406             }
53407             if(this.config.animate && skipAnim !== true){
53408                 this.fireEvent("invalidated", this);
53409                 this.animateCollapse();
53410             }else{
53411                 this.el.setLocation(-20000,-20000);
53412                 this.el.hide();
53413                 this.collapsedEl.show();
53414                 this.fireEvent("collapsed", this);
53415                 this.fireEvent("invalidated", this);
53416             }
53417         }
53418         
53419     },
53420
53421     animateCollapse : function(){
53422         // overridden
53423     },
53424
53425     /**
53426      * Expands this region if it was previously collapsed.
53427      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53428      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53429      */
53430     expand : function(e, skipAnim){
53431         if(e) {
53432             e.stopPropagation();
53433         }
53434         if(!this.collapsed || this.el.hasActiveFx()) {
53435             return;
53436         }
53437         if(this.isSlid){
53438             this.afterSlideIn();
53439             skipAnim = true;
53440         }
53441         this.collapsed = false;
53442         if(this.config.animate && skipAnim !== true){
53443             this.animateExpand();
53444         }else{
53445             this.el.show();
53446             if(this.split){
53447                 this.split.el.show();
53448             }
53449             this.collapsedEl.setLocation(-2000,-2000);
53450             this.collapsedEl.hide();
53451             this.fireEvent("invalidated", this);
53452             this.fireEvent("expanded", this);
53453         }
53454     },
53455
53456     animateExpand : function(){
53457         // overridden
53458     },
53459
53460     initTabs : function()
53461     {
53462         this.bodyEl.setStyle("overflow", "hidden");
53463         var ts = new Roo.TabPanel(
53464                 this.bodyEl.dom,
53465                 {
53466                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53467                     disableTooltips: this.config.disableTabTips,
53468                     toolbar : this.config.toolbar
53469                 }
53470         );
53471         if(this.config.hideTabs){
53472             ts.stripWrap.setDisplayed(false);
53473         }
53474         this.tabs = ts;
53475         ts.resizeTabs = this.config.resizeTabs === true;
53476         ts.minTabWidth = this.config.minTabWidth || 40;
53477         ts.maxTabWidth = this.config.maxTabWidth || 250;
53478         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53479         ts.monitorResize = false;
53480         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53481         ts.bodyEl.addClass('x-layout-tabs-body');
53482         this.panels.each(this.initPanelAsTab, this);
53483     },
53484
53485     initPanelAsTab : function(panel){
53486         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53487                     this.config.closeOnTab && panel.isClosable());
53488         if(panel.tabTip !== undefined){
53489             ti.setTooltip(panel.tabTip);
53490         }
53491         ti.on("activate", function(){
53492               this.setActivePanel(panel);
53493         }, this);
53494         if(this.config.closeOnTab){
53495             ti.on("beforeclose", function(t, e){
53496                 e.cancel = true;
53497                 this.remove(panel);
53498             }, this);
53499         }
53500         return ti;
53501     },
53502
53503     updatePanelTitle : function(panel, title){
53504         if(this.activePanel == panel){
53505             this.updateTitle(title);
53506         }
53507         if(this.tabs){
53508             var ti = this.tabs.getTab(panel.getEl().id);
53509             ti.setText(title);
53510             if(panel.tabTip !== undefined){
53511                 ti.setTooltip(panel.tabTip);
53512             }
53513         }
53514     },
53515
53516     updateTitle : function(title){
53517         if(this.titleTextEl && !this.config.title){
53518             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53519         }
53520     },
53521
53522     setActivePanel : function(panel){
53523         panel = this.getPanel(panel);
53524         if(this.activePanel && this.activePanel != panel){
53525             this.activePanel.setActiveState(false);
53526         }
53527         this.activePanel = panel;
53528         panel.setActiveState(true);
53529         if(this.panelSize){
53530             panel.setSize(this.panelSize.width, this.panelSize.height);
53531         }
53532         if(this.closeBtn){
53533             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53534         }
53535         this.updateTitle(panel.getTitle());
53536         if(this.tabs){
53537             this.fireEvent("invalidated", this);
53538         }
53539         this.fireEvent("panelactivated", this, panel);
53540     },
53541
53542     /**
53543      * Shows the specified panel.
53544      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53545      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53546      */
53547     showPanel : function(panel)
53548     {
53549         panel = this.getPanel(panel);
53550         if(panel){
53551             if(this.tabs){
53552                 var tab = this.tabs.getTab(panel.getEl().id);
53553                 if(tab.isHidden()){
53554                     this.tabs.unhideTab(tab.id);
53555                 }
53556                 tab.activate();
53557             }else{
53558                 this.setActivePanel(panel);
53559             }
53560         }
53561         return panel;
53562     },
53563
53564     /**
53565      * Get the active panel for this region.
53566      * @return {Roo.ContentPanel} The active panel or null
53567      */
53568     getActivePanel : function(){
53569         return this.activePanel;
53570     },
53571
53572     validateVisibility : function(){
53573         if(this.panels.getCount() < 1){
53574             this.updateTitle("&#160;");
53575             this.closeBtn.hide();
53576             this.hide();
53577         }else{
53578             if(!this.isVisible()){
53579                 this.show();
53580             }
53581         }
53582     },
53583
53584     /**
53585      * Adds the passed ContentPanel(s) to this region.
53586      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53587      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53588      */
53589     add : function(panel){
53590         if(arguments.length > 1){
53591             for(var i = 0, len = arguments.length; i < len; i++) {
53592                 this.add(arguments[i]);
53593             }
53594             return null;
53595         }
53596         if(this.hasPanel(panel)){
53597             this.showPanel(panel);
53598             return panel;
53599         }
53600         panel.setRegion(this);
53601         this.panels.add(panel);
53602         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53603             this.bodyEl.dom.appendChild(panel.getEl().dom);
53604             if(panel.background !== true){
53605                 this.setActivePanel(panel);
53606             }
53607             this.fireEvent("paneladded", this, panel);
53608             return panel;
53609         }
53610         if(!this.tabs){
53611             this.initTabs();
53612         }else{
53613             this.initPanelAsTab(panel);
53614         }
53615         if(panel.background !== true){
53616             this.tabs.activate(panel.getEl().id);
53617         }
53618         this.fireEvent("paneladded", this, panel);
53619         return panel;
53620     },
53621
53622     /**
53623      * Hides the tab for the specified panel.
53624      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53625      */
53626     hidePanel : function(panel){
53627         if(this.tabs && (panel = this.getPanel(panel))){
53628             this.tabs.hideTab(panel.getEl().id);
53629         }
53630     },
53631
53632     /**
53633      * Unhides the tab for a previously hidden panel.
53634      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53635      */
53636     unhidePanel : function(panel){
53637         if(this.tabs && (panel = this.getPanel(panel))){
53638             this.tabs.unhideTab(panel.getEl().id);
53639         }
53640     },
53641
53642     clearPanels : function(){
53643         while(this.panels.getCount() > 0){
53644              this.remove(this.panels.first());
53645         }
53646     },
53647
53648     /**
53649      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53650      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53651      * @param {Boolean} preservePanel Overrides the config preservePanel option
53652      * @return {Roo.ContentPanel} The panel that was removed
53653      */
53654     remove : function(panel, preservePanel){
53655         panel = this.getPanel(panel);
53656         if(!panel){
53657             return null;
53658         }
53659         var e = {};
53660         this.fireEvent("beforeremove", this, panel, e);
53661         if(e.cancel === true){
53662             return null;
53663         }
53664         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53665         var panelId = panel.getId();
53666         this.panels.removeKey(panelId);
53667         if(preservePanel){
53668             document.body.appendChild(panel.getEl().dom);
53669         }
53670         if(this.tabs){
53671             this.tabs.removeTab(panel.getEl().id);
53672         }else if (!preservePanel){
53673             this.bodyEl.dom.removeChild(panel.getEl().dom);
53674         }
53675         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53676             var p = this.panels.first();
53677             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53678             tempEl.appendChild(p.getEl().dom);
53679             this.bodyEl.update("");
53680             this.bodyEl.dom.appendChild(p.getEl().dom);
53681             tempEl = null;
53682             this.updateTitle(p.getTitle());
53683             this.tabs = null;
53684             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53685             this.setActivePanel(p);
53686         }
53687         panel.setRegion(null);
53688         if(this.activePanel == panel){
53689             this.activePanel = null;
53690         }
53691         if(this.config.autoDestroy !== false && preservePanel !== true){
53692             try{panel.destroy();}catch(e){}
53693         }
53694         this.fireEvent("panelremoved", this, panel);
53695         return panel;
53696     },
53697
53698     /**
53699      * Returns the TabPanel component used by this region
53700      * @return {Roo.TabPanel}
53701      */
53702     getTabs : function(){
53703         return this.tabs;
53704     },
53705
53706     createTool : function(parentEl, className){
53707         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53708             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53709         btn.addClassOnOver("x-layout-tools-button-over");
53710         return btn;
53711     }
53712 });/*
53713  * Based on:
53714  * Ext JS Library 1.1.1
53715  * Copyright(c) 2006-2007, Ext JS, LLC.
53716  *
53717  * Originally Released Under LGPL - original licence link has changed is not relivant.
53718  *
53719  * Fork - LGPL
53720  * <script type="text/javascript">
53721  */
53722  
53723
53724
53725 /**
53726  * @class Roo.SplitLayoutRegion
53727  * @extends Roo.LayoutRegion
53728  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53729  */
53730 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53731     this.cursor = cursor;
53732     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53733 };
53734
53735 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53736     splitTip : "Drag to resize.",
53737     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53738     useSplitTips : false,
53739
53740     applyConfig : function(config){
53741         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53742         if(config.split){
53743             if(!this.split){
53744                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53745                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53746                 /** The SplitBar for this region 
53747                 * @type Roo.SplitBar */
53748                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53749                 this.split.on("moved", this.onSplitMove, this);
53750                 this.split.useShim = config.useShim === true;
53751                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53752                 if(this.useSplitTips){
53753                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53754                 }
53755                 if(config.collapsible){
53756                     this.split.el.on("dblclick", this.collapse,  this);
53757                 }
53758             }
53759             if(typeof config.minSize != "undefined"){
53760                 this.split.minSize = config.minSize;
53761             }
53762             if(typeof config.maxSize != "undefined"){
53763                 this.split.maxSize = config.maxSize;
53764             }
53765             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53766                 this.hideSplitter();
53767             }
53768         }
53769     },
53770
53771     getHMaxSize : function(){
53772          var cmax = this.config.maxSize || 10000;
53773          var center = this.mgr.getRegion("center");
53774          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53775     },
53776
53777     getVMaxSize : function(){
53778          var cmax = this.config.maxSize || 10000;
53779          var center = this.mgr.getRegion("center");
53780          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53781     },
53782
53783     onSplitMove : function(split, newSize){
53784         this.fireEvent("resized", this, newSize);
53785     },
53786     
53787     /** 
53788      * Returns the {@link Roo.SplitBar} for this region.
53789      * @return {Roo.SplitBar}
53790      */
53791     getSplitBar : function(){
53792         return this.split;
53793     },
53794     
53795     hide : function(){
53796         this.hideSplitter();
53797         Roo.SplitLayoutRegion.superclass.hide.call(this);
53798     },
53799
53800     hideSplitter : function(){
53801         if(this.split){
53802             this.split.el.setLocation(-2000,-2000);
53803             this.split.el.hide();
53804         }
53805     },
53806
53807     show : function(){
53808         if(this.split){
53809             this.split.el.show();
53810         }
53811         Roo.SplitLayoutRegion.superclass.show.call(this);
53812     },
53813     
53814     beforeSlide: function(){
53815         if(Roo.isGecko){// firefox overflow auto bug workaround
53816             this.bodyEl.clip();
53817             if(this.tabs) {
53818                 this.tabs.bodyEl.clip();
53819             }
53820             if(this.activePanel){
53821                 this.activePanel.getEl().clip();
53822                 
53823                 if(this.activePanel.beforeSlide){
53824                     this.activePanel.beforeSlide();
53825                 }
53826             }
53827         }
53828     },
53829     
53830     afterSlide : function(){
53831         if(Roo.isGecko){// firefox overflow auto bug workaround
53832             this.bodyEl.unclip();
53833             if(this.tabs) {
53834                 this.tabs.bodyEl.unclip();
53835             }
53836             if(this.activePanel){
53837                 this.activePanel.getEl().unclip();
53838                 if(this.activePanel.afterSlide){
53839                     this.activePanel.afterSlide();
53840                 }
53841             }
53842         }
53843     },
53844
53845     initAutoHide : function(){
53846         if(this.autoHide !== false){
53847             if(!this.autoHideHd){
53848                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53849                 this.autoHideHd = {
53850                     "mouseout": function(e){
53851                         if(!e.within(this.el, true)){
53852                             st.delay(500);
53853                         }
53854                     },
53855                     "mouseover" : function(e){
53856                         st.cancel();
53857                     },
53858                     scope : this
53859                 };
53860             }
53861             this.el.on(this.autoHideHd);
53862         }
53863     },
53864
53865     clearAutoHide : function(){
53866         if(this.autoHide !== false){
53867             this.el.un("mouseout", this.autoHideHd.mouseout);
53868             this.el.un("mouseover", this.autoHideHd.mouseover);
53869         }
53870     },
53871
53872     clearMonitor : function(){
53873         Roo.get(document).un("click", this.slideInIf, this);
53874     },
53875
53876     // these names are backwards but not changed for compat
53877     slideOut : function(){
53878         if(this.isSlid || this.el.hasActiveFx()){
53879             return;
53880         }
53881         this.isSlid = true;
53882         if(this.collapseBtn){
53883             this.collapseBtn.hide();
53884         }
53885         this.closeBtnState = this.closeBtn.getStyle('display');
53886         this.closeBtn.hide();
53887         if(this.stickBtn){
53888             this.stickBtn.show();
53889         }
53890         this.el.show();
53891         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53892         this.beforeSlide();
53893         this.el.setStyle("z-index", 10001);
53894         this.el.slideIn(this.getSlideAnchor(), {
53895             callback: function(){
53896                 this.afterSlide();
53897                 this.initAutoHide();
53898                 Roo.get(document).on("click", this.slideInIf, this);
53899                 this.fireEvent("slideshow", this);
53900             },
53901             scope: this,
53902             block: true
53903         });
53904     },
53905
53906     afterSlideIn : function(){
53907         this.clearAutoHide();
53908         this.isSlid = false;
53909         this.clearMonitor();
53910         this.el.setStyle("z-index", "");
53911         if(this.collapseBtn){
53912             this.collapseBtn.show();
53913         }
53914         this.closeBtn.setStyle('display', this.closeBtnState);
53915         if(this.stickBtn){
53916             this.stickBtn.hide();
53917         }
53918         this.fireEvent("slidehide", this);
53919     },
53920
53921     slideIn : function(cb){
53922         if(!this.isSlid || this.el.hasActiveFx()){
53923             Roo.callback(cb);
53924             return;
53925         }
53926         this.isSlid = false;
53927         this.beforeSlide();
53928         this.el.slideOut(this.getSlideAnchor(), {
53929             callback: function(){
53930                 this.el.setLeftTop(-10000, -10000);
53931                 this.afterSlide();
53932                 this.afterSlideIn();
53933                 Roo.callback(cb);
53934             },
53935             scope: this,
53936             block: true
53937         });
53938     },
53939     
53940     slideInIf : function(e){
53941         if(!e.within(this.el)){
53942             this.slideIn();
53943         }
53944     },
53945
53946     animateCollapse : function(){
53947         this.beforeSlide();
53948         this.el.setStyle("z-index", 20000);
53949         var anchor = this.getSlideAnchor();
53950         this.el.slideOut(anchor, {
53951             callback : function(){
53952                 this.el.setStyle("z-index", "");
53953                 this.collapsedEl.slideIn(anchor, {duration:.3});
53954                 this.afterSlide();
53955                 this.el.setLocation(-10000,-10000);
53956                 this.el.hide();
53957                 this.fireEvent("collapsed", this);
53958             },
53959             scope: this,
53960             block: true
53961         });
53962     },
53963
53964     animateExpand : function(){
53965         this.beforeSlide();
53966         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53967         this.el.setStyle("z-index", 20000);
53968         this.collapsedEl.hide({
53969             duration:.1
53970         });
53971         this.el.slideIn(this.getSlideAnchor(), {
53972             callback : function(){
53973                 this.el.setStyle("z-index", "");
53974                 this.afterSlide();
53975                 if(this.split){
53976                     this.split.el.show();
53977                 }
53978                 this.fireEvent("invalidated", this);
53979                 this.fireEvent("expanded", this);
53980             },
53981             scope: this,
53982             block: true
53983         });
53984     },
53985
53986     anchors : {
53987         "west" : "left",
53988         "east" : "right",
53989         "north" : "top",
53990         "south" : "bottom"
53991     },
53992
53993     sanchors : {
53994         "west" : "l",
53995         "east" : "r",
53996         "north" : "t",
53997         "south" : "b"
53998     },
53999
54000     canchors : {
54001         "west" : "tl-tr",
54002         "east" : "tr-tl",
54003         "north" : "tl-bl",
54004         "south" : "bl-tl"
54005     },
54006
54007     getAnchor : function(){
54008         return this.anchors[this.position];
54009     },
54010
54011     getCollapseAnchor : function(){
54012         return this.canchors[this.position];
54013     },
54014
54015     getSlideAnchor : function(){
54016         return this.sanchors[this.position];
54017     },
54018
54019     getAlignAdj : function(){
54020         var cm = this.cmargins;
54021         switch(this.position){
54022             case "west":
54023                 return [0, 0];
54024             break;
54025             case "east":
54026                 return [0, 0];
54027             break;
54028             case "north":
54029                 return [0, 0];
54030             break;
54031             case "south":
54032                 return [0, 0];
54033             break;
54034         }
54035     },
54036
54037     getExpandAdj : function(){
54038         var c = this.collapsedEl, cm = this.cmargins;
54039         switch(this.position){
54040             case "west":
54041                 return [-(cm.right+c.getWidth()+cm.left), 0];
54042             break;
54043             case "east":
54044                 return [cm.right+c.getWidth()+cm.left, 0];
54045             break;
54046             case "north":
54047                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54048             break;
54049             case "south":
54050                 return [0, cm.top+cm.bottom+c.getHeight()];
54051             break;
54052         }
54053     }
54054 });/*
54055  * Based on:
54056  * Ext JS Library 1.1.1
54057  * Copyright(c) 2006-2007, Ext JS, LLC.
54058  *
54059  * Originally Released Under LGPL - original licence link has changed is not relivant.
54060  *
54061  * Fork - LGPL
54062  * <script type="text/javascript">
54063  */
54064 /*
54065  * These classes are private internal classes
54066  */
54067 Roo.CenterLayoutRegion = function(mgr, config){
54068     Roo.LayoutRegion.call(this, mgr, config, "center");
54069     this.visible = true;
54070     this.minWidth = config.minWidth || 20;
54071     this.minHeight = config.minHeight || 20;
54072 };
54073
54074 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54075     hide : function(){
54076         // center panel can't be hidden
54077     },
54078     
54079     show : function(){
54080         // center panel can't be hidden
54081     },
54082     
54083     getMinWidth: function(){
54084         return this.minWidth;
54085     },
54086     
54087     getMinHeight: function(){
54088         return this.minHeight;
54089     }
54090 });
54091
54092
54093 Roo.NorthLayoutRegion = function(mgr, config){
54094     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54095     if(this.split){
54096         this.split.placement = Roo.SplitBar.TOP;
54097         this.split.orientation = Roo.SplitBar.VERTICAL;
54098         this.split.el.addClass("x-layout-split-v");
54099     }
54100     var size = config.initialSize || config.height;
54101     if(typeof size != "undefined"){
54102         this.el.setHeight(size);
54103     }
54104 };
54105 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54106     orientation: Roo.SplitBar.VERTICAL,
54107     getBox : function(){
54108         if(this.collapsed){
54109             return this.collapsedEl.getBox();
54110         }
54111         var box = this.el.getBox();
54112         if(this.split){
54113             box.height += this.split.el.getHeight();
54114         }
54115         return box;
54116     },
54117     
54118     updateBox : function(box){
54119         if(this.split && !this.collapsed){
54120             box.height -= this.split.el.getHeight();
54121             this.split.el.setLeft(box.x);
54122             this.split.el.setTop(box.y+box.height);
54123             this.split.el.setWidth(box.width);
54124         }
54125         if(this.collapsed){
54126             this.updateBody(box.width, null);
54127         }
54128         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54129     }
54130 });
54131
54132 Roo.SouthLayoutRegion = function(mgr, config){
54133     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54134     if(this.split){
54135         this.split.placement = Roo.SplitBar.BOTTOM;
54136         this.split.orientation = Roo.SplitBar.VERTICAL;
54137         this.split.el.addClass("x-layout-split-v");
54138     }
54139     var size = config.initialSize || config.height;
54140     if(typeof size != "undefined"){
54141         this.el.setHeight(size);
54142     }
54143 };
54144 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54145     orientation: Roo.SplitBar.VERTICAL,
54146     getBox : function(){
54147         if(this.collapsed){
54148             return this.collapsedEl.getBox();
54149         }
54150         var box = this.el.getBox();
54151         if(this.split){
54152             var sh = this.split.el.getHeight();
54153             box.height += sh;
54154             box.y -= sh;
54155         }
54156         return box;
54157     },
54158     
54159     updateBox : function(box){
54160         if(this.split && !this.collapsed){
54161             var sh = this.split.el.getHeight();
54162             box.height -= sh;
54163             box.y += sh;
54164             this.split.el.setLeft(box.x);
54165             this.split.el.setTop(box.y-sh);
54166             this.split.el.setWidth(box.width);
54167         }
54168         if(this.collapsed){
54169             this.updateBody(box.width, null);
54170         }
54171         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54172     }
54173 });
54174
54175 Roo.EastLayoutRegion = function(mgr, config){
54176     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54177     if(this.split){
54178         this.split.placement = Roo.SplitBar.RIGHT;
54179         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54180         this.split.el.addClass("x-layout-split-h");
54181     }
54182     var size = config.initialSize || config.width;
54183     if(typeof size != "undefined"){
54184         this.el.setWidth(size);
54185     }
54186 };
54187 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54188     orientation: Roo.SplitBar.HORIZONTAL,
54189     getBox : function(){
54190         if(this.collapsed){
54191             return this.collapsedEl.getBox();
54192         }
54193         var box = this.el.getBox();
54194         if(this.split){
54195             var sw = this.split.el.getWidth();
54196             box.width += sw;
54197             box.x -= sw;
54198         }
54199         return box;
54200     },
54201
54202     updateBox : function(box){
54203         if(this.split && !this.collapsed){
54204             var sw = this.split.el.getWidth();
54205             box.width -= sw;
54206             this.split.el.setLeft(box.x);
54207             this.split.el.setTop(box.y);
54208             this.split.el.setHeight(box.height);
54209             box.x += sw;
54210         }
54211         if(this.collapsed){
54212             this.updateBody(null, box.height);
54213         }
54214         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54215     }
54216 });
54217
54218 Roo.WestLayoutRegion = function(mgr, config){
54219     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54220     if(this.split){
54221         this.split.placement = Roo.SplitBar.LEFT;
54222         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54223         this.split.el.addClass("x-layout-split-h");
54224     }
54225     var size = config.initialSize || config.width;
54226     if(typeof size != "undefined"){
54227         this.el.setWidth(size);
54228     }
54229 };
54230 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54231     orientation: Roo.SplitBar.HORIZONTAL,
54232     getBox : function(){
54233         if(this.collapsed){
54234             return this.collapsedEl.getBox();
54235         }
54236         var box = this.el.getBox();
54237         if(this.split){
54238             box.width += this.split.el.getWidth();
54239         }
54240         return box;
54241     },
54242     
54243     updateBox : function(box){
54244         if(this.split && !this.collapsed){
54245             var sw = this.split.el.getWidth();
54246             box.width -= sw;
54247             this.split.el.setLeft(box.x+box.width);
54248             this.split.el.setTop(box.y);
54249             this.split.el.setHeight(box.height);
54250         }
54251         if(this.collapsed){
54252             this.updateBody(null, box.height);
54253         }
54254         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54255     }
54256 });
54257 /*
54258  * Based on:
54259  * Ext JS Library 1.1.1
54260  * Copyright(c) 2006-2007, Ext JS, LLC.
54261  *
54262  * Originally Released Under LGPL - original licence link has changed is not relivant.
54263  *
54264  * Fork - LGPL
54265  * <script type="text/javascript">
54266  */
54267  
54268  
54269 /*
54270  * Private internal class for reading and applying state
54271  */
54272 Roo.LayoutStateManager = function(layout){
54273      // default empty state
54274      this.state = {
54275         north: {},
54276         south: {},
54277         east: {},
54278         west: {}       
54279     };
54280 };
54281
54282 Roo.LayoutStateManager.prototype = {
54283     init : function(layout, provider){
54284         this.provider = provider;
54285         var state = provider.get(layout.id+"-layout-state");
54286         if(state){
54287             var wasUpdating = layout.isUpdating();
54288             if(!wasUpdating){
54289                 layout.beginUpdate();
54290             }
54291             for(var key in state){
54292                 if(typeof state[key] != "function"){
54293                     var rstate = state[key];
54294                     var r = layout.getRegion(key);
54295                     if(r && rstate){
54296                         if(rstate.size){
54297                             r.resizeTo(rstate.size);
54298                         }
54299                         if(rstate.collapsed == true){
54300                             r.collapse(true);
54301                         }else{
54302                             r.expand(null, true);
54303                         }
54304                     }
54305                 }
54306             }
54307             if(!wasUpdating){
54308                 layout.endUpdate();
54309             }
54310             this.state = state; 
54311         }
54312         this.layout = layout;
54313         layout.on("regionresized", this.onRegionResized, this);
54314         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54315         layout.on("regionexpanded", this.onRegionExpanded, this);
54316     },
54317     
54318     storeState : function(){
54319         this.provider.set(this.layout.id+"-layout-state", this.state);
54320     },
54321     
54322     onRegionResized : function(region, newSize){
54323         this.state[region.getPosition()].size = newSize;
54324         this.storeState();
54325     },
54326     
54327     onRegionCollapsed : function(region){
54328         this.state[region.getPosition()].collapsed = true;
54329         this.storeState();
54330     },
54331     
54332     onRegionExpanded : function(region){
54333         this.state[region.getPosition()].collapsed = false;
54334         this.storeState();
54335     }
54336 };/*
54337  * Based on:
54338  * Ext JS Library 1.1.1
54339  * Copyright(c) 2006-2007, Ext JS, LLC.
54340  *
54341  * Originally Released Under LGPL - original licence link has changed is not relivant.
54342  *
54343  * Fork - LGPL
54344  * <script type="text/javascript">
54345  */
54346 /**
54347  * @class Roo.ContentPanel
54348  * @extends Roo.util.Observable
54349  * A basic ContentPanel element.
54350  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54351  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54352  * @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
54353  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54354  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54355  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54356  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54357  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54358  * @cfg {String} title          The title for this panel
54359  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54360  * @cfg {String} url            Calls {@link #setUrl} with this value
54361  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54362  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54363  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54364  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54365  * @cfg {String}    style  Extra style to add to the content panel 
54366
54367  * @constructor
54368  * Create a new ContentPanel.
54369  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54370  * @param {String/Object} config A string to set only the title or a config object
54371  * @param {String} content (optional) Set the HTML content for this panel
54372  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54373  */
54374 Roo.ContentPanel = function(el, config, content){
54375     
54376      
54377     /*
54378     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54379         config = el;
54380         el = Roo.id();
54381     }
54382     if (config && config.parentLayout) { 
54383         el = config.parentLayout.el.createChild(); 
54384     }
54385     */
54386     if(el.autoCreate){ // xtype is available if this is called from factory
54387         config = el;
54388         el = Roo.id();
54389     }
54390     this.el = Roo.get(el);
54391     if(!this.el && config && config.autoCreate){
54392         if(typeof config.autoCreate == "object"){
54393             if(!config.autoCreate.id){
54394                 config.autoCreate.id = config.id||el;
54395             }
54396             this.el = Roo.DomHelper.append(document.body,
54397                         config.autoCreate, true);
54398         }else{
54399             this.el = Roo.DomHelper.append(document.body,
54400                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54401         }
54402     }
54403     
54404     
54405     this.closable = false;
54406     this.loaded = false;
54407     this.active = false;
54408     if(typeof config == "string"){
54409         this.title = config;
54410     }else{
54411         Roo.apply(this, config);
54412     }
54413     
54414     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54415         this.wrapEl = this.el.wrap();
54416         this.toolbar.container = this.el.insertSibling(false, 'before');
54417         this.toolbar = new Roo.Toolbar(this.toolbar);
54418     }
54419     
54420     // xtype created footer. - not sure if will work as we normally have to render first..
54421     if (this.footer && !this.footer.el && this.footer.xtype) {
54422         if (!this.wrapEl) {
54423             this.wrapEl = this.el.wrap();
54424         }
54425     
54426         this.footer.container = this.wrapEl.createChild();
54427          
54428         this.footer = Roo.factory(this.footer, Roo);
54429         
54430     }
54431     
54432     if(this.resizeEl){
54433         this.resizeEl = Roo.get(this.resizeEl, true);
54434     }else{
54435         this.resizeEl = this.el;
54436     }
54437     // handle view.xtype
54438     
54439  
54440     
54441     
54442     this.addEvents({
54443         /**
54444          * @event activate
54445          * Fires when this panel is activated. 
54446          * @param {Roo.ContentPanel} this
54447          */
54448         "activate" : true,
54449         /**
54450          * @event deactivate
54451          * Fires when this panel is activated. 
54452          * @param {Roo.ContentPanel} this
54453          */
54454         "deactivate" : true,
54455
54456         /**
54457          * @event resize
54458          * Fires when this panel is resized if fitToFrame is true.
54459          * @param {Roo.ContentPanel} this
54460          * @param {Number} width The width after any component adjustments
54461          * @param {Number} height The height after any component adjustments
54462          */
54463         "resize" : true,
54464         
54465          /**
54466          * @event render
54467          * Fires when this tab is created
54468          * @param {Roo.ContentPanel} this
54469          */
54470         "render" : true
54471          
54472         
54473     });
54474     
54475
54476     
54477     
54478     if(this.autoScroll){
54479         this.resizeEl.setStyle("overflow", "auto");
54480     } else {
54481         // fix randome scrolling
54482         this.el.on('scroll', function() {
54483             Roo.log('fix random scolling');
54484             this.scrollTo('top',0); 
54485         });
54486     }
54487     content = content || this.content;
54488     if(content){
54489         this.setContent(content);
54490     }
54491     if(config && config.url){
54492         this.setUrl(this.url, this.params, this.loadOnce);
54493     }
54494     
54495     
54496     
54497     Roo.ContentPanel.superclass.constructor.call(this);
54498     
54499     if (this.view && typeof(this.view.xtype) != 'undefined') {
54500         this.view.el = this.el.appendChild(document.createElement("div"));
54501         this.view = Roo.factory(this.view); 
54502         this.view.render  &&  this.view.render(false, '');  
54503     }
54504     
54505     
54506     this.fireEvent('render', this);
54507 };
54508
54509 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54510     tabTip:'',
54511     setRegion : function(region){
54512         this.region = region;
54513         if(region){
54514            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54515         }else{
54516            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54517         } 
54518     },
54519     
54520     /**
54521      * Returns the toolbar for this Panel if one was configured. 
54522      * @return {Roo.Toolbar} 
54523      */
54524     getToolbar : function(){
54525         return this.toolbar;
54526     },
54527     
54528     setActiveState : function(active){
54529         this.active = active;
54530         if(!active){
54531             this.fireEvent("deactivate", this);
54532         }else{
54533             this.fireEvent("activate", this);
54534         }
54535     },
54536     /**
54537      * Updates this panel's element
54538      * @param {String} content The new content
54539      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54540     */
54541     setContent : function(content, loadScripts){
54542         this.el.update(content, loadScripts);
54543     },
54544
54545     ignoreResize : function(w, h){
54546         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54547             return true;
54548         }else{
54549             this.lastSize = {width: w, height: h};
54550             return false;
54551         }
54552     },
54553     /**
54554      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54555      * @return {Roo.UpdateManager} The UpdateManager
54556      */
54557     getUpdateManager : function(){
54558         return this.el.getUpdateManager();
54559     },
54560      /**
54561      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54562      * @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:
54563 <pre><code>
54564 panel.load({
54565     url: "your-url.php",
54566     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54567     callback: yourFunction,
54568     scope: yourObject, //(optional scope)
54569     discardUrl: false,
54570     nocache: false,
54571     text: "Loading...",
54572     timeout: 30,
54573     scripts: false
54574 });
54575 </code></pre>
54576      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54577      * 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.
54578      * @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}
54579      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54580      * @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.
54581      * @return {Roo.ContentPanel} this
54582      */
54583     load : function(){
54584         var um = this.el.getUpdateManager();
54585         um.update.apply(um, arguments);
54586         return this;
54587     },
54588
54589
54590     /**
54591      * 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.
54592      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54593      * @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)
54594      * @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)
54595      * @return {Roo.UpdateManager} The UpdateManager
54596      */
54597     setUrl : function(url, params, loadOnce){
54598         if(this.refreshDelegate){
54599             this.removeListener("activate", this.refreshDelegate);
54600         }
54601         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54602         this.on("activate", this.refreshDelegate);
54603         return this.el.getUpdateManager();
54604     },
54605     
54606     _handleRefresh : function(url, params, loadOnce){
54607         if(!loadOnce || !this.loaded){
54608             var updater = this.el.getUpdateManager();
54609             updater.update(url, params, this._setLoaded.createDelegate(this));
54610         }
54611     },
54612     
54613     _setLoaded : function(){
54614         this.loaded = true;
54615     }, 
54616     
54617     /**
54618      * Returns this panel's id
54619      * @return {String} 
54620      */
54621     getId : function(){
54622         return this.el.id;
54623     },
54624     
54625     /** 
54626      * Returns this panel's element - used by regiosn to add.
54627      * @return {Roo.Element} 
54628      */
54629     getEl : function(){
54630         return this.wrapEl || this.el;
54631     },
54632     
54633     adjustForComponents : function(width, height)
54634     {
54635         //Roo.log('adjustForComponents ');
54636         if(this.resizeEl != this.el){
54637             width -= this.el.getFrameWidth('lr');
54638             height -= this.el.getFrameWidth('tb');
54639         }
54640         if(this.toolbar){
54641             var te = this.toolbar.getEl();
54642             height -= te.getHeight();
54643             te.setWidth(width);
54644         }
54645         if(this.footer){
54646             var te = this.footer.getEl();
54647             //Roo.log("footer:" + te.getHeight());
54648             
54649             height -= te.getHeight();
54650             te.setWidth(width);
54651         }
54652         
54653         
54654         if(this.adjustments){
54655             width += this.adjustments[0];
54656             height += this.adjustments[1];
54657         }
54658         return {"width": width, "height": height};
54659     },
54660     
54661     setSize : function(width, height){
54662         if(this.fitToFrame && !this.ignoreResize(width, height)){
54663             if(this.fitContainer && this.resizeEl != this.el){
54664                 this.el.setSize(width, height);
54665             }
54666             var size = this.adjustForComponents(width, height);
54667             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54668             this.fireEvent('resize', this, size.width, size.height);
54669         }
54670     },
54671     
54672     /**
54673      * Returns this panel's title
54674      * @return {String} 
54675      */
54676     getTitle : function(){
54677         return this.title;
54678     },
54679     
54680     /**
54681      * Set this panel's title
54682      * @param {String} title
54683      */
54684     setTitle : function(title){
54685         this.title = title;
54686         if(this.region){
54687             this.region.updatePanelTitle(this, title);
54688         }
54689     },
54690     
54691     /**
54692      * Returns true is this panel was configured to be closable
54693      * @return {Boolean} 
54694      */
54695     isClosable : function(){
54696         return this.closable;
54697     },
54698     
54699     beforeSlide : function(){
54700         this.el.clip();
54701         this.resizeEl.clip();
54702     },
54703     
54704     afterSlide : function(){
54705         this.el.unclip();
54706         this.resizeEl.unclip();
54707     },
54708     
54709     /**
54710      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54711      *   Will fail silently if the {@link #setUrl} method has not been called.
54712      *   This does not activate the panel, just updates its content.
54713      */
54714     refresh : function(){
54715         if(this.refreshDelegate){
54716            this.loaded = false;
54717            this.refreshDelegate();
54718         }
54719     },
54720     
54721     /**
54722      * Destroys this panel
54723      */
54724     destroy : function(){
54725         this.el.removeAllListeners();
54726         var tempEl = document.createElement("span");
54727         tempEl.appendChild(this.el.dom);
54728         tempEl.innerHTML = "";
54729         this.el.remove();
54730         this.el = null;
54731     },
54732     
54733     /**
54734      * form - if the content panel contains a form - this is a reference to it.
54735      * @type {Roo.form.Form}
54736      */
54737     form : false,
54738     /**
54739      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54740      *    This contains a reference to it.
54741      * @type {Roo.View}
54742      */
54743     view : false,
54744     
54745       /**
54746      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54747      * <pre><code>
54748
54749 layout.addxtype({
54750        xtype : 'Form',
54751        items: [ .... ]
54752    }
54753 );
54754
54755 </code></pre>
54756      * @param {Object} cfg Xtype definition of item to add.
54757      */
54758     
54759     addxtype : function(cfg) {
54760         // add form..
54761         if (cfg.xtype.match(/^Form$/)) {
54762             
54763             var el;
54764             //if (this.footer) {
54765             //    el = this.footer.container.insertSibling(false, 'before');
54766             //} else {
54767                 el = this.el.createChild();
54768             //}
54769
54770             this.form = new  Roo.form.Form(cfg);
54771             
54772             
54773             if ( this.form.allItems.length) {
54774                 this.form.render(el.dom);
54775             }
54776             return this.form;
54777         }
54778         // should only have one of theses..
54779         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54780             // views.. should not be just added - used named prop 'view''
54781             
54782             cfg.el = this.el.appendChild(document.createElement("div"));
54783             // factory?
54784             
54785             var ret = new Roo.factory(cfg);
54786              
54787              ret.render && ret.render(false, ''); // render blank..
54788             this.view = ret;
54789             return ret;
54790         }
54791         return false;
54792     }
54793 });
54794
54795 /**
54796  * @class Roo.GridPanel
54797  * @extends Roo.ContentPanel
54798  * @constructor
54799  * Create a new GridPanel.
54800  * @param {Roo.grid.Grid} grid The grid for this panel
54801  * @param {String/Object} config A string to set only the panel's title, or a config object
54802  */
54803 Roo.GridPanel = function(grid, config){
54804     
54805   
54806     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54807         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54808         
54809     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54810     
54811     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54812     
54813     if(this.toolbar){
54814         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54815     }
54816     // xtype created footer. - not sure if will work as we normally have to render first..
54817     if (this.footer && !this.footer.el && this.footer.xtype) {
54818         
54819         this.footer.container = this.grid.getView().getFooterPanel(true);
54820         this.footer.dataSource = this.grid.dataSource;
54821         this.footer = Roo.factory(this.footer, Roo);
54822         
54823     }
54824     
54825     grid.monitorWindowResize = false; // turn off autosizing
54826     grid.autoHeight = false;
54827     grid.autoWidth = false;
54828     this.grid = grid;
54829     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54830 };
54831
54832 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54833     getId : function(){
54834         return this.grid.id;
54835     },
54836     
54837     /**
54838      * Returns the grid for this panel
54839      * @return {Roo.grid.Grid} 
54840      */
54841     getGrid : function(){
54842         return this.grid;    
54843     },
54844     
54845     setSize : function(width, height){
54846         if(!this.ignoreResize(width, height)){
54847             var grid = this.grid;
54848             var size = this.adjustForComponents(width, height);
54849             grid.getGridEl().setSize(size.width, size.height);
54850             grid.autoSize();
54851         }
54852     },
54853     
54854     beforeSlide : function(){
54855         this.grid.getView().scroller.clip();
54856     },
54857     
54858     afterSlide : function(){
54859         this.grid.getView().scroller.unclip();
54860     },
54861     
54862     destroy : function(){
54863         this.grid.destroy();
54864         delete this.grid;
54865         Roo.GridPanel.superclass.destroy.call(this); 
54866     }
54867 });
54868
54869
54870 /**
54871  * @class Roo.NestedLayoutPanel
54872  * @extends Roo.ContentPanel
54873  * @constructor
54874  * Create a new NestedLayoutPanel.
54875  * 
54876  * 
54877  * @param {Roo.BorderLayout} layout The layout for this panel
54878  * @param {String/Object} config A string to set only the title or a config object
54879  */
54880 Roo.NestedLayoutPanel = function(layout, config)
54881 {
54882     // construct with only one argument..
54883     /* FIXME - implement nicer consturctors
54884     if (layout.layout) {
54885         config = layout;
54886         layout = config.layout;
54887         delete config.layout;
54888     }
54889     if (layout.xtype && !layout.getEl) {
54890         // then layout needs constructing..
54891         layout = Roo.factory(layout, Roo);
54892     }
54893     */
54894     
54895     
54896     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54897     
54898     layout.monitorWindowResize = false; // turn off autosizing
54899     this.layout = layout;
54900     this.layout.getEl().addClass("x-layout-nested-layout");
54901     
54902     
54903     
54904     
54905 };
54906
54907 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54908
54909     setSize : function(width, height){
54910         if(!this.ignoreResize(width, height)){
54911             var size = this.adjustForComponents(width, height);
54912             var el = this.layout.getEl();
54913             el.setSize(size.width, size.height);
54914             var touch = el.dom.offsetWidth;
54915             this.layout.layout();
54916             // ie requires a double layout on the first pass
54917             if(Roo.isIE && !this.initialized){
54918                 this.initialized = true;
54919                 this.layout.layout();
54920             }
54921         }
54922     },
54923     
54924     // activate all subpanels if not currently active..
54925     
54926     setActiveState : function(active){
54927         this.active = active;
54928         if(!active){
54929             this.fireEvent("deactivate", this);
54930             return;
54931         }
54932         
54933         this.fireEvent("activate", this);
54934         // not sure if this should happen before or after..
54935         if (!this.layout) {
54936             return; // should not happen..
54937         }
54938         var reg = false;
54939         for (var r in this.layout.regions) {
54940             reg = this.layout.getRegion(r);
54941             if (reg.getActivePanel()) {
54942                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54943                 reg.setActivePanel(reg.getActivePanel());
54944                 continue;
54945             }
54946             if (!reg.panels.length) {
54947                 continue;
54948             }
54949             reg.showPanel(reg.getPanel(0));
54950         }
54951         
54952         
54953         
54954         
54955     },
54956     
54957     /**
54958      * Returns the nested BorderLayout for this panel
54959      * @return {Roo.BorderLayout} 
54960      */
54961     getLayout : function(){
54962         return this.layout;
54963     },
54964     
54965      /**
54966      * Adds a xtype elements to the layout of the nested panel
54967      * <pre><code>
54968
54969 panel.addxtype({
54970        xtype : 'ContentPanel',
54971        region: 'west',
54972        items: [ .... ]
54973    }
54974 );
54975
54976 panel.addxtype({
54977         xtype : 'NestedLayoutPanel',
54978         region: 'west',
54979         layout: {
54980            center: { },
54981            west: { }   
54982         },
54983         items : [ ... list of content panels or nested layout panels.. ]
54984    }
54985 );
54986 </code></pre>
54987      * @param {Object} cfg Xtype definition of item to add.
54988      */
54989     addxtype : function(cfg) {
54990         return this.layout.addxtype(cfg);
54991     
54992     }
54993 });
54994
54995 Roo.ScrollPanel = function(el, config, content){
54996     config = config || {};
54997     config.fitToFrame = true;
54998     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54999     
55000     this.el.dom.style.overflow = "hidden";
55001     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55002     this.el.removeClass("x-layout-inactive-content");
55003     this.el.on("mousewheel", this.onWheel, this);
55004
55005     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55006     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55007     up.unselectable(); down.unselectable();
55008     up.on("click", this.scrollUp, this);
55009     down.on("click", this.scrollDown, this);
55010     up.addClassOnOver("x-scroller-btn-over");
55011     down.addClassOnOver("x-scroller-btn-over");
55012     up.addClassOnClick("x-scroller-btn-click");
55013     down.addClassOnClick("x-scroller-btn-click");
55014     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55015
55016     this.resizeEl = this.el;
55017     this.el = wrap; this.up = up; this.down = down;
55018 };
55019
55020 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55021     increment : 100,
55022     wheelIncrement : 5,
55023     scrollUp : function(){
55024         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55025     },
55026
55027     scrollDown : function(){
55028         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55029     },
55030
55031     afterScroll : function(){
55032         var el = this.resizeEl;
55033         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55034         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55035         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55036     },
55037
55038     setSize : function(){
55039         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55040         this.afterScroll();
55041     },
55042
55043     onWheel : function(e){
55044         var d = e.getWheelDelta();
55045         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55046         this.afterScroll();
55047         e.stopEvent();
55048     },
55049
55050     setContent : function(content, loadScripts){
55051         this.resizeEl.update(content, loadScripts);
55052     }
55053
55054 });
55055
55056
55057
55058
55059
55060
55061
55062
55063
55064 /**
55065  * @class Roo.TreePanel
55066  * @extends Roo.ContentPanel
55067  * @constructor
55068  * Create a new TreePanel. - defaults to fit/scoll contents.
55069  * @param {String/Object} config A string to set only the panel's title, or a config object
55070  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55071  */
55072 Roo.TreePanel = function(config){
55073     var el = config.el;
55074     var tree = config.tree;
55075     delete config.tree; 
55076     delete config.el; // hopefull!
55077     
55078     // wrapper for IE7 strict & safari scroll issue
55079     
55080     var treeEl = el.createChild();
55081     config.resizeEl = treeEl;
55082     
55083     
55084     
55085     Roo.TreePanel.superclass.constructor.call(this, el, config);
55086  
55087  
55088     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55089     //console.log(tree);
55090     this.on('activate', function()
55091     {
55092         if (this.tree.rendered) {
55093             return;
55094         }
55095         //console.log('render tree');
55096         this.tree.render();
55097     });
55098     // this should not be needed.. - it's actually the 'el' that resizes?
55099     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55100     
55101     //this.on('resize',  function (cp, w, h) {
55102     //        this.tree.innerCt.setWidth(w);
55103     //        this.tree.innerCt.setHeight(h);
55104     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55105     //});
55106
55107         
55108     
55109 };
55110
55111 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55112     fitToFrame : true,
55113     autoScroll : true
55114 });
55115
55116
55117
55118
55119
55120
55121
55122
55123
55124
55125
55126 /*
55127  * Based on:
55128  * Ext JS Library 1.1.1
55129  * Copyright(c) 2006-2007, Ext JS, LLC.
55130  *
55131  * Originally Released Under LGPL - original licence link has changed is not relivant.
55132  *
55133  * Fork - LGPL
55134  * <script type="text/javascript">
55135  */
55136  
55137
55138 /**
55139  * @class Roo.ReaderLayout
55140  * @extends Roo.BorderLayout
55141  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55142  * center region containing two nested regions (a top one for a list view and one for item preview below),
55143  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55144  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55145  * expedites the setup of the overall layout and regions for this common application style.
55146  * Example:
55147  <pre><code>
55148 var reader = new Roo.ReaderLayout();
55149 var CP = Roo.ContentPanel;  // shortcut for adding
55150
55151 reader.beginUpdate();
55152 reader.add("north", new CP("north", "North"));
55153 reader.add("west", new CP("west", {title: "West"}));
55154 reader.add("east", new CP("east", {title: "East"}));
55155
55156 reader.regions.listView.add(new CP("listView", "List"));
55157 reader.regions.preview.add(new CP("preview", "Preview"));
55158 reader.endUpdate();
55159 </code></pre>
55160 * @constructor
55161 * Create a new ReaderLayout
55162 * @param {Object} config Configuration options
55163 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55164 * document.body if omitted)
55165 */
55166 Roo.ReaderLayout = function(config, renderTo){
55167     var c = config || {size:{}};
55168     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55169         north: c.north !== false ? Roo.apply({
55170             split:false,
55171             initialSize: 32,
55172             titlebar: false
55173         }, c.north) : false,
55174         west: c.west !== false ? Roo.apply({
55175             split:true,
55176             initialSize: 200,
55177             minSize: 175,
55178             maxSize: 400,
55179             titlebar: true,
55180             collapsible: true,
55181             animate: true,
55182             margins:{left:5,right:0,bottom:5,top:5},
55183             cmargins:{left:5,right:5,bottom:5,top:5}
55184         }, c.west) : false,
55185         east: c.east !== false ? Roo.apply({
55186             split:true,
55187             initialSize: 200,
55188             minSize: 175,
55189             maxSize: 400,
55190             titlebar: true,
55191             collapsible: true,
55192             animate: true,
55193             margins:{left:0,right:5,bottom:5,top:5},
55194             cmargins:{left:5,right:5,bottom:5,top:5}
55195         }, c.east) : false,
55196         center: Roo.apply({
55197             tabPosition: 'top',
55198             autoScroll:false,
55199             closeOnTab: true,
55200             titlebar:false,
55201             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55202         }, c.center)
55203     });
55204
55205     this.el.addClass('x-reader');
55206
55207     this.beginUpdate();
55208
55209     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55210         south: c.preview !== false ? Roo.apply({
55211             split:true,
55212             initialSize: 200,
55213             minSize: 100,
55214             autoScroll:true,
55215             collapsible:true,
55216             titlebar: true,
55217             cmargins:{top:5,left:0, right:0, bottom:0}
55218         }, c.preview) : false,
55219         center: Roo.apply({
55220             autoScroll:false,
55221             titlebar:false,
55222             minHeight:200
55223         }, c.listView)
55224     });
55225     this.add('center', new Roo.NestedLayoutPanel(inner,
55226             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55227
55228     this.endUpdate();
55229
55230     this.regions.preview = inner.getRegion('south');
55231     this.regions.listView = inner.getRegion('center');
55232 };
55233
55234 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55235  * Based on:
55236  * Ext JS Library 1.1.1
55237  * Copyright(c) 2006-2007, Ext JS, LLC.
55238  *
55239  * Originally Released Under LGPL - original licence link has changed is not relivant.
55240  *
55241  * Fork - LGPL
55242  * <script type="text/javascript">
55243  */
55244  
55245 /**
55246  * @class Roo.grid.Grid
55247  * @extends Roo.util.Observable
55248  * This class represents the primary interface of a component based grid control.
55249  * <br><br>Usage:<pre><code>
55250  var grid = new Roo.grid.Grid("my-container-id", {
55251      ds: myDataStore,
55252      cm: myColModel,
55253      selModel: mySelectionModel,
55254      autoSizeColumns: true,
55255      monitorWindowResize: false,
55256      trackMouseOver: true
55257  });
55258  // set any options
55259  grid.render();
55260  * </code></pre>
55261  * <b>Common Problems:</b><br/>
55262  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55263  * element will correct this<br/>
55264  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55265  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55266  * are unpredictable.<br/>
55267  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55268  * grid to calculate dimensions/offsets.<br/>
55269   * @constructor
55270  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55271  * The container MUST have some type of size defined for the grid to fill. The container will be
55272  * automatically set to position relative if it isn't already.
55273  * @param {Object} config A config object that sets properties on this grid.
55274  */
55275 Roo.grid.Grid = function(container, config){
55276         // initialize the container
55277         this.container = Roo.get(container);
55278         this.container.update("");
55279         this.container.setStyle("overflow", "hidden");
55280     this.container.addClass('x-grid-container');
55281
55282     this.id = this.container.id;
55283
55284     Roo.apply(this, config);
55285     // check and correct shorthanded configs
55286     if(this.ds){
55287         this.dataSource = this.ds;
55288         delete this.ds;
55289     }
55290     if(this.cm){
55291         this.colModel = this.cm;
55292         delete this.cm;
55293     }
55294     if(this.sm){
55295         this.selModel = this.sm;
55296         delete this.sm;
55297     }
55298
55299     if (this.selModel) {
55300         this.selModel = Roo.factory(this.selModel, Roo.grid);
55301         this.sm = this.selModel;
55302         this.sm.xmodule = this.xmodule || false;
55303     }
55304     if (typeof(this.colModel.config) == 'undefined') {
55305         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55306         this.cm = this.colModel;
55307         this.cm.xmodule = this.xmodule || false;
55308     }
55309     if (this.dataSource) {
55310         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55311         this.ds = this.dataSource;
55312         this.ds.xmodule = this.xmodule || false;
55313          
55314     }
55315     
55316     
55317     
55318     if(this.width){
55319         this.container.setWidth(this.width);
55320     }
55321
55322     if(this.height){
55323         this.container.setHeight(this.height);
55324     }
55325     /** @private */
55326         this.addEvents({
55327         // raw events
55328         /**
55329          * @event click
55330          * The raw click event for the entire grid.
55331          * @param {Roo.EventObject} e
55332          */
55333         "click" : true,
55334         /**
55335          * @event dblclick
55336          * The raw dblclick event for the entire grid.
55337          * @param {Roo.EventObject} e
55338          */
55339         "dblclick" : true,
55340         /**
55341          * @event contextmenu
55342          * The raw contextmenu event for the entire grid.
55343          * @param {Roo.EventObject} e
55344          */
55345         "contextmenu" : true,
55346         /**
55347          * @event mousedown
55348          * The raw mousedown event for the entire grid.
55349          * @param {Roo.EventObject} e
55350          */
55351         "mousedown" : true,
55352         /**
55353          * @event mouseup
55354          * The raw mouseup event for the entire grid.
55355          * @param {Roo.EventObject} e
55356          */
55357         "mouseup" : true,
55358         /**
55359          * @event mouseover
55360          * The raw mouseover event for the entire grid.
55361          * @param {Roo.EventObject} e
55362          */
55363         "mouseover" : true,
55364         /**
55365          * @event mouseout
55366          * The raw mouseout event for the entire grid.
55367          * @param {Roo.EventObject} e
55368          */
55369         "mouseout" : true,
55370         /**
55371          * @event keypress
55372          * The raw keypress event for the entire grid.
55373          * @param {Roo.EventObject} e
55374          */
55375         "keypress" : true,
55376         /**
55377          * @event keydown
55378          * The raw keydown event for the entire grid.
55379          * @param {Roo.EventObject} e
55380          */
55381         "keydown" : true,
55382
55383         // custom events
55384
55385         /**
55386          * @event cellclick
55387          * Fires when a cell is clicked
55388          * @param {Grid} this
55389          * @param {Number} rowIndex
55390          * @param {Number} columnIndex
55391          * @param {Roo.EventObject} e
55392          */
55393         "cellclick" : true,
55394         /**
55395          * @event celldblclick
55396          * Fires when a cell is double clicked
55397          * @param {Grid} this
55398          * @param {Number} rowIndex
55399          * @param {Number} columnIndex
55400          * @param {Roo.EventObject} e
55401          */
55402         "celldblclick" : true,
55403         /**
55404          * @event rowclick
55405          * Fires when a row is clicked
55406          * @param {Grid} this
55407          * @param {Number} rowIndex
55408          * @param {Roo.EventObject} e
55409          */
55410         "rowclick" : true,
55411         /**
55412          * @event rowdblclick
55413          * Fires when a row is double clicked
55414          * @param {Grid} this
55415          * @param {Number} rowIndex
55416          * @param {Roo.EventObject} e
55417          */
55418         "rowdblclick" : true,
55419         /**
55420          * @event headerclick
55421          * Fires when a header is clicked
55422          * @param {Grid} this
55423          * @param {Number} columnIndex
55424          * @param {Roo.EventObject} e
55425          */
55426         "headerclick" : true,
55427         /**
55428          * @event headerdblclick
55429          * Fires when a header cell is double clicked
55430          * @param {Grid} this
55431          * @param {Number} columnIndex
55432          * @param {Roo.EventObject} e
55433          */
55434         "headerdblclick" : true,
55435         /**
55436          * @event rowcontextmenu
55437          * Fires when a row is right clicked
55438          * @param {Grid} this
55439          * @param {Number} rowIndex
55440          * @param {Roo.EventObject} e
55441          */
55442         "rowcontextmenu" : true,
55443         /**
55444          * @event cellcontextmenu
55445          * Fires when a cell is right clicked
55446          * @param {Grid} this
55447          * @param {Number} rowIndex
55448          * @param {Number} cellIndex
55449          * @param {Roo.EventObject} e
55450          */
55451          "cellcontextmenu" : true,
55452         /**
55453          * @event headercontextmenu
55454          * Fires when a header is right clicked
55455          * @param {Grid} this
55456          * @param {Number} columnIndex
55457          * @param {Roo.EventObject} e
55458          */
55459         "headercontextmenu" : true,
55460         /**
55461          * @event bodyscroll
55462          * Fires when the body element is scrolled
55463          * @param {Number} scrollLeft
55464          * @param {Number} scrollTop
55465          */
55466         "bodyscroll" : true,
55467         /**
55468          * @event columnresize
55469          * Fires when the user resizes a column
55470          * @param {Number} columnIndex
55471          * @param {Number} newSize
55472          */
55473         "columnresize" : true,
55474         /**
55475          * @event columnmove
55476          * Fires when the user moves a column
55477          * @param {Number} oldIndex
55478          * @param {Number} newIndex
55479          */
55480         "columnmove" : true,
55481         /**
55482          * @event startdrag
55483          * Fires when row(s) start being dragged
55484          * @param {Grid} this
55485          * @param {Roo.GridDD} dd The drag drop object
55486          * @param {event} e The raw browser event
55487          */
55488         "startdrag" : true,
55489         /**
55490          * @event enddrag
55491          * Fires when a drag operation is complete
55492          * @param {Grid} this
55493          * @param {Roo.GridDD} dd The drag drop object
55494          * @param {event} e The raw browser event
55495          */
55496         "enddrag" : true,
55497         /**
55498          * @event dragdrop
55499          * Fires when dragged row(s) are dropped on a valid DD target
55500          * @param {Grid} this
55501          * @param {Roo.GridDD} dd The drag drop object
55502          * @param {String} targetId The target drag drop object
55503          * @param {event} e The raw browser event
55504          */
55505         "dragdrop" : true,
55506         /**
55507          * @event dragover
55508          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55509          * @param {Grid} this
55510          * @param {Roo.GridDD} dd The drag drop object
55511          * @param {String} targetId The target drag drop object
55512          * @param {event} e The raw browser event
55513          */
55514         "dragover" : true,
55515         /**
55516          * @event dragenter
55517          *  Fires when the dragged row(s) first cross another DD target while being dragged
55518          * @param {Grid} this
55519          * @param {Roo.GridDD} dd The drag drop object
55520          * @param {String} targetId The target drag drop object
55521          * @param {event} e The raw browser event
55522          */
55523         "dragenter" : true,
55524         /**
55525          * @event dragout
55526          * Fires when the dragged row(s) leave another DD target while being dragged
55527          * @param {Grid} this
55528          * @param {Roo.GridDD} dd The drag drop object
55529          * @param {String} targetId The target drag drop object
55530          * @param {event} e The raw browser event
55531          */
55532         "dragout" : true,
55533         /**
55534          * @event rowclass
55535          * Fires when a row is rendered, so you can change add a style to it.
55536          * @param {GridView} gridview   The grid view
55537          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55538          */
55539         'rowclass' : true,
55540
55541         /**
55542          * @event render
55543          * Fires when the grid is rendered
55544          * @param {Grid} grid
55545          */
55546         'render' : true
55547     });
55548
55549     Roo.grid.Grid.superclass.constructor.call(this);
55550 };
55551 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55552     
55553     /**
55554      * @cfg {String} ddGroup - drag drop group.
55555      */
55556       /**
55557      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55558      */
55559
55560     /**
55561      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55562      */
55563     minColumnWidth : 25,
55564
55565     /**
55566      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55567      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55568      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55569      */
55570     autoSizeColumns : false,
55571
55572     /**
55573      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55574      */
55575     autoSizeHeaders : true,
55576
55577     /**
55578      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55579      */
55580     monitorWindowResize : true,
55581
55582     /**
55583      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55584      * rows measured to get a columns size. Default is 0 (all rows).
55585      */
55586     maxRowsToMeasure : 0,
55587
55588     /**
55589      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55590      */
55591     trackMouseOver : true,
55592
55593     /**
55594     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55595     */
55596       /**
55597     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55598     */
55599     
55600     /**
55601     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55602     */
55603     enableDragDrop : false,
55604     
55605     /**
55606     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55607     */
55608     enableColumnMove : true,
55609     
55610     /**
55611     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55612     */
55613     enableColumnHide : true,
55614     
55615     /**
55616     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55617     */
55618     enableRowHeightSync : false,
55619     
55620     /**
55621     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55622     */
55623     stripeRows : true,
55624     
55625     /**
55626     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55627     */
55628     autoHeight : false,
55629
55630     /**
55631      * @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.
55632      */
55633     autoExpandColumn : false,
55634
55635     /**
55636     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55637     * Default is 50.
55638     */
55639     autoExpandMin : 50,
55640
55641     /**
55642     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55643     */
55644     autoExpandMax : 1000,
55645
55646     /**
55647     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55648     */
55649     view : null,
55650
55651     /**
55652     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55653     */
55654     loadMask : false,
55655     /**
55656     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55657     */
55658     dropTarget: false,
55659     
55660    
55661     
55662     // private
55663     rendered : false,
55664
55665     /**
55666     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55667     * of a fixed width. Default is false.
55668     */
55669     /**
55670     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55671     */
55672     
55673     
55674     /**
55675     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55676     * %0 is replaced with the number of selected rows.
55677     */
55678     ddText : "{0} selected row{1}",
55679     
55680     
55681     /**
55682      * Called once after all setup has been completed and the grid is ready to be rendered.
55683      * @return {Roo.grid.Grid} this
55684      */
55685     render : function()
55686     {
55687         var c = this.container;
55688         // try to detect autoHeight/width mode
55689         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55690             this.autoHeight = true;
55691         }
55692         var view = this.getView();
55693         view.init(this);
55694
55695         c.on("click", this.onClick, this);
55696         c.on("dblclick", this.onDblClick, this);
55697         c.on("contextmenu", this.onContextMenu, this);
55698         c.on("keydown", this.onKeyDown, this);
55699         if (Roo.isTouch) {
55700             c.on("touchstart", this.onTouchStart, this);
55701         }
55702
55703         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55704
55705         this.getSelectionModel().init(this);
55706
55707         view.render();
55708
55709         if(this.loadMask){
55710             this.loadMask = new Roo.LoadMask(this.container,
55711                     Roo.apply({store:this.dataSource}, this.loadMask));
55712         }
55713         
55714         
55715         if (this.toolbar && this.toolbar.xtype) {
55716             this.toolbar.container = this.getView().getHeaderPanel(true);
55717             this.toolbar = new Roo.Toolbar(this.toolbar);
55718         }
55719         if (this.footer && this.footer.xtype) {
55720             this.footer.dataSource = this.getDataSource();
55721             this.footer.container = this.getView().getFooterPanel(true);
55722             this.footer = Roo.factory(this.footer, Roo);
55723         }
55724         if (this.dropTarget && this.dropTarget.xtype) {
55725             delete this.dropTarget.xtype;
55726             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55727         }
55728         
55729         
55730         this.rendered = true;
55731         this.fireEvent('render', this);
55732         return this;
55733     },
55734
55735     /**
55736      * Reconfigures the grid to use a different Store and Column Model.
55737      * The View will be bound to the new objects and refreshed.
55738      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55739      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55740      */
55741     reconfigure : function(dataSource, colModel){
55742         if(this.loadMask){
55743             this.loadMask.destroy();
55744             this.loadMask = new Roo.LoadMask(this.container,
55745                     Roo.apply({store:dataSource}, this.loadMask));
55746         }
55747         this.view.bind(dataSource, colModel);
55748         this.dataSource = dataSource;
55749         this.colModel = colModel;
55750         this.view.refresh(true);
55751     },
55752     /**
55753      * addColumns
55754      * Add's a column, default at the end..
55755      
55756      * @param {int} position to add (default end)
55757      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55758      */
55759     addColumns : function(pos, ar)
55760     {
55761         
55762         for (var i =0;i< ar.length;i++) {
55763             var cfg = ar[i];
55764             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55765             this.cm.lookup[cfg.id] = cfg;
55766         }
55767         
55768         
55769         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55770             pos = this.cm.config.length; //this.cm.config.push(cfg);
55771         } 
55772         pos = Math.max(0,pos);
55773         ar.unshift(0);
55774         ar.unshift(pos);
55775         this.cm.config.splice.apply(this.cm.config, ar);
55776         
55777         
55778         
55779         this.view.generateRules(this.cm);
55780         this.view.refresh(true);
55781         
55782     },
55783     
55784     
55785     
55786     
55787     // private
55788     onKeyDown : function(e){
55789         this.fireEvent("keydown", e);
55790     },
55791
55792     /**
55793      * Destroy this grid.
55794      * @param {Boolean} removeEl True to remove the element
55795      */
55796     destroy : function(removeEl, keepListeners){
55797         if(this.loadMask){
55798             this.loadMask.destroy();
55799         }
55800         var c = this.container;
55801         c.removeAllListeners();
55802         this.view.destroy();
55803         this.colModel.purgeListeners();
55804         if(!keepListeners){
55805             this.purgeListeners();
55806         }
55807         c.update("");
55808         if(removeEl === true){
55809             c.remove();
55810         }
55811     },
55812
55813     // private
55814     processEvent : function(name, e){
55815         // does this fire select???
55816         //Roo.log('grid:processEvent '  + name);
55817         
55818         if (name != 'touchstart' ) {
55819             this.fireEvent(name, e);    
55820         }
55821         
55822         var t = e.getTarget();
55823         var v = this.view;
55824         var header = v.findHeaderIndex(t);
55825         if(header !== false){
55826             var ename = name == 'touchstart' ? 'click' : name;
55827              
55828             this.fireEvent("header" + ename, this, header, e);
55829         }else{
55830             var row = v.findRowIndex(t);
55831             var cell = v.findCellIndex(t);
55832             if (name == 'touchstart') {
55833                 // first touch is always a click.
55834                 // hopefull this happens after selection is updated.?
55835                 name = false;
55836                 
55837                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55838                     var cs = this.selModel.getSelectedCell();
55839                     if (row == cs[0] && cell == cs[1]){
55840                         name = 'dblclick';
55841                     }
55842                 }
55843                 if (typeof(this.selModel.getSelections) != 'undefined') {
55844                     var cs = this.selModel.getSelections();
55845                     var ds = this.dataSource;
55846                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55847                         name = 'dblclick';
55848                     }
55849                 }
55850                 if (!name) {
55851                     return;
55852                 }
55853             }
55854             
55855             
55856             if(row !== false){
55857                 this.fireEvent("row" + name, this, row, e);
55858                 if(cell !== false){
55859                     this.fireEvent("cell" + name, this, row, cell, e);
55860                 }
55861             }
55862         }
55863     },
55864
55865     // private
55866     onClick : function(e){
55867         this.processEvent("click", e);
55868     },
55869    // private
55870     onTouchStart : function(e){
55871         this.processEvent("touchstart", e);
55872     },
55873
55874     // private
55875     onContextMenu : function(e, t){
55876         this.processEvent("contextmenu", e);
55877     },
55878
55879     // private
55880     onDblClick : function(e){
55881         this.processEvent("dblclick", e);
55882     },
55883
55884     // private
55885     walkCells : function(row, col, step, fn, scope){
55886         var cm = this.colModel, clen = cm.getColumnCount();
55887         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55888         if(step < 0){
55889             if(col < 0){
55890                 row--;
55891                 first = false;
55892             }
55893             while(row >= 0){
55894                 if(!first){
55895                     col = clen-1;
55896                 }
55897                 first = false;
55898                 while(col >= 0){
55899                     if(fn.call(scope || this, row, col, cm) === true){
55900                         return [row, col];
55901                     }
55902                     col--;
55903                 }
55904                 row--;
55905             }
55906         } else {
55907             if(col >= clen){
55908                 row++;
55909                 first = false;
55910             }
55911             while(row < rlen){
55912                 if(!first){
55913                     col = 0;
55914                 }
55915                 first = false;
55916                 while(col < clen){
55917                     if(fn.call(scope || this, row, col, cm) === true){
55918                         return [row, col];
55919                     }
55920                     col++;
55921                 }
55922                 row++;
55923             }
55924         }
55925         return null;
55926     },
55927
55928     // private
55929     getSelections : function(){
55930         return this.selModel.getSelections();
55931     },
55932
55933     /**
55934      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55935      * but if manual update is required this method will initiate it.
55936      */
55937     autoSize : function(){
55938         if(this.rendered){
55939             this.view.layout();
55940             if(this.view.adjustForScroll){
55941                 this.view.adjustForScroll();
55942             }
55943         }
55944     },
55945
55946     /**
55947      * Returns the grid's underlying element.
55948      * @return {Element} The element
55949      */
55950     getGridEl : function(){
55951         return this.container;
55952     },
55953
55954     // private for compatibility, overridden by editor grid
55955     stopEditing : function(){},
55956
55957     /**
55958      * Returns the grid's SelectionModel.
55959      * @return {SelectionModel}
55960      */
55961     getSelectionModel : function(){
55962         if(!this.selModel){
55963             this.selModel = new Roo.grid.RowSelectionModel();
55964         }
55965         return this.selModel;
55966     },
55967
55968     /**
55969      * Returns the grid's DataSource.
55970      * @return {DataSource}
55971      */
55972     getDataSource : function(){
55973         return this.dataSource;
55974     },
55975
55976     /**
55977      * Returns the grid's ColumnModel.
55978      * @return {ColumnModel}
55979      */
55980     getColumnModel : function(){
55981         return this.colModel;
55982     },
55983
55984     /**
55985      * Returns the grid's GridView object.
55986      * @return {GridView}
55987      */
55988     getView : function(){
55989         if(!this.view){
55990             this.view = new Roo.grid.GridView(this.viewConfig);
55991             this.relayEvents(this.view, [
55992                 "beforerowremoved", "beforerowsinserted",
55993                 "beforerefresh", "rowremoved",
55994                 "rowsinserted", "rowupdated" ,"refresh"
55995             ]);
55996         }
55997         return this.view;
55998     },
55999     /**
56000      * Called to get grid's drag proxy text, by default returns this.ddText.
56001      * Override this to put something different in the dragged text.
56002      * @return {String}
56003      */
56004     getDragDropText : function(){
56005         var count = this.selModel.getCount();
56006         return String.format(this.ddText, count, count == 1 ? '' : 's');
56007     }
56008 });
56009 /*
56010  * Based on:
56011  * Ext JS Library 1.1.1
56012  * Copyright(c) 2006-2007, Ext JS, LLC.
56013  *
56014  * Originally Released Under LGPL - original licence link has changed is not relivant.
56015  *
56016  * Fork - LGPL
56017  * <script type="text/javascript">
56018  */
56019  
56020 Roo.grid.AbstractGridView = function(){
56021         this.grid = null;
56022         
56023         this.events = {
56024             "beforerowremoved" : true,
56025             "beforerowsinserted" : true,
56026             "beforerefresh" : true,
56027             "rowremoved" : true,
56028             "rowsinserted" : true,
56029             "rowupdated" : true,
56030             "refresh" : true
56031         };
56032     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56033 };
56034
56035 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56036     rowClass : "x-grid-row",
56037     cellClass : "x-grid-cell",
56038     tdClass : "x-grid-td",
56039     hdClass : "x-grid-hd",
56040     splitClass : "x-grid-hd-split",
56041     
56042     init: function(grid){
56043         this.grid = grid;
56044                 var cid = this.grid.getGridEl().id;
56045         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56046         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56047         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56048         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56049         },
56050         
56051     getColumnRenderers : function(){
56052         var renderers = [];
56053         var cm = this.grid.colModel;
56054         var colCount = cm.getColumnCount();
56055         for(var i = 0; i < colCount; i++){
56056             renderers[i] = cm.getRenderer(i);
56057         }
56058         return renderers;
56059     },
56060     
56061     getColumnIds : function(){
56062         var ids = [];
56063         var cm = this.grid.colModel;
56064         var colCount = cm.getColumnCount();
56065         for(var i = 0; i < colCount; i++){
56066             ids[i] = cm.getColumnId(i);
56067         }
56068         return ids;
56069     },
56070     
56071     getDataIndexes : function(){
56072         if(!this.indexMap){
56073             this.indexMap = this.buildIndexMap();
56074         }
56075         return this.indexMap.colToData;
56076     },
56077     
56078     getColumnIndexByDataIndex : function(dataIndex){
56079         if(!this.indexMap){
56080             this.indexMap = this.buildIndexMap();
56081         }
56082         return this.indexMap.dataToCol[dataIndex];
56083     },
56084     
56085     /**
56086      * Set a css style for a column dynamically. 
56087      * @param {Number} colIndex The index of the column
56088      * @param {String} name The css property name
56089      * @param {String} value The css value
56090      */
56091     setCSSStyle : function(colIndex, name, value){
56092         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56093         Roo.util.CSS.updateRule(selector, name, value);
56094     },
56095     
56096     generateRules : function(cm){
56097         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56098         Roo.util.CSS.removeStyleSheet(rulesId);
56099         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56100             var cid = cm.getColumnId(i);
56101             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56102                          this.tdSelector, cid, " {\n}\n",
56103                          this.hdSelector, cid, " {\n}\n",
56104                          this.splitSelector, cid, " {\n}\n");
56105         }
56106         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56107     }
56108 });/*
56109  * Based on:
56110  * Ext JS Library 1.1.1
56111  * Copyright(c) 2006-2007, Ext JS, LLC.
56112  *
56113  * Originally Released Under LGPL - original licence link has changed is not relivant.
56114  *
56115  * Fork - LGPL
56116  * <script type="text/javascript">
56117  */
56118
56119 // private
56120 // This is a support class used internally by the Grid components
56121 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56122     this.grid = grid;
56123     this.view = grid.getView();
56124     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56125     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56126     if(hd2){
56127         this.setHandleElId(Roo.id(hd));
56128         this.setOuterHandleElId(Roo.id(hd2));
56129     }
56130     this.scroll = false;
56131 };
56132 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56133     maxDragWidth: 120,
56134     getDragData : function(e){
56135         var t = Roo.lib.Event.getTarget(e);
56136         var h = this.view.findHeaderCell(t);
56137         if(h){
56138             return {ddel: h.firstChild, header:h};
56139         }
56140         return false;
56141     },
56142
56143     onInitDrag : function(e){
56144         this.view.headersDisabled = true;
56145         var clone = this.dragData.ddel.cloneNode(true);
56146         clone.id = Roo.id();
56147         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56148         this.proxy.update(clone);
56149         return true;
56150     },
56151
56152     afterValidDrop : function(){
56153         var v = this.view;
56154         setTimeout(function(){
56155             v.headersDisabled = false;
56156         }, 50);
56157     },
56158
56159     afterInvalidDrop : function(){
56160         var v = this.view;
56161         setTimeout(function(){
56162             v.headersDisabled = false;
56163         }, 50);
56164     }
56165 });
56166 /*
56167  * Based on:
56168  * Ext JS Library 1.1.1
56169  * Copyright(c) 2006-2007, Ext JS, LLC.
56170  *
56171  * Originally Released Under LGPL - original licence link has changed is not relivant.
56172  *
56173  * Fork - LGPL
56174  * <script type="text/javascript">
56175  */
56176 // private
56177 // This is a support class used internally by the Grid components
56178 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56179     this.grid = grid;
56180     this.view = grid.getView();
56181     // split the proxies so they don't interfere with mouse events
56182     this.proxyTop = Roo.DomHelper.append(document.body, {
56183         cls:"col-move-top", html:"&#160;"
56184     }, true);
56185     this.proxyBottom = Roo.DomHelper.append(document.body, {
56186         cls:"col-move-bottom", html:"&#160;"
56187     }, true);
56188     this.proxyTop.hide = this.proxyBottom.hide = function(){
56189         this.setLeftTop(-100,-100);
56190         this.setStyle("visibility", "hidden");
56191     };
56192     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56193     // temporarily disabled
56194     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56195     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56196 };
56197 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56198     proxyOffsets : [-4, -9],
56199     fly: Roo.Element.fly,
56200
56201     getTargetFromEvent : function(e){
56202         var t = Roo.lib.Event.getTarget(e);
56203         var cindex = this.view.findCellIndex(t);
56204         if(cindex !== false){
56205             return this.view.getHeaderCell(cindex);
56206         }
56207         return null;
56208     },
56209
56210     nextVisible : function(h){
56211         var v = this.view, cm = this.grid.colModel;
56212         h = h.nextSibling;
56213         while(h){
56214             if(!cm.isHidden(v.getCellIndex(h))){
56215                 return h;
56216             }
56217             h = h.nextSibling;
56218         }
56219         return null;
56220     },
56221
56222     prevVisible : function(h){
56223         var v = this.view, cm = this.grid.colModel;
56224         h = h.prevSibling;
56225         while(h){
56226             if(!cm.isHidden(v.getCellIndex(h))){
56227                 return h;
56228             }
56229             h = h.prevSibling;
56230         }
56231         return null;
56232     },
56233
56234     positionIndicator : function(h, n, e){
56235         var x = Roo.lib.Event.getPageX(e);
56236         var r = Roo.lib.Dom.getRegion(n.firstChild);
56237         var px, pt, py = r.top + this.proxyOffsets[1];
56238         if((r.right - x) <= (r.right-r.left)/2){
56239             px = r.right+this.view.borderWidth;
56240             pt = "after";
56241         }else{
56242             px = r.left;
56243             pt = "before";
56244         }
56245         var oldIndex = this.view.getCellIndex(h);
56246         var newIndex = this.view.getCellIndex(n);
56247
56248         if(this.grid.colModel.isFixed(newIndex)){
56249             return false;
56250         }
56251
56252         var locked = this.grid.colModel.isLocked(newIndex);
56253
56254         if(pt == "after"){
56255             newIndex++;
56256         }
56257         if(oldIndex < newIndex){
56258             newIndex--;
56259         }
56260         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56261             return false;
56262         }
56263         px +=  this.proxyOffsets[0];
56264         this.proxyTop.setLeftTop(px, py);
56265         this.proxyTop.show();
56266         if(!this.bottomOffset){
56267             this.bottomOffset = this.view.mainHd.getHeight();
56268         }
56269         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56270         this.proxyBottom.show();
56271         return pt;
56272     },
56273
56274     onNodeEnter : function(n, dd, e, data){
56275         if(data.header != n){
56276             this.positionIndicator(data.header, n, e);
56277         }
56278     },
56279
56280     onNodeOver : function(n, dd, e, data){
56281         var result = false;
56282         if(data.header != n){
56283             result = this.positionIndicator(data.header, n, e);
56284         }
56285         if(!result){
56286             this.proxyTop.hide();
56287             this.proxyBottom.hide();
56288         }
56289         return result ? this.dropAllowed : this.dropNotAllowed;
56290     },
56291
56292     onNodeOut : function(n, dd, e, data){
56293         this.proxyTop.hide();
56294         this.proxyBottom.hide();
56295     },
56296
56297     onNodeDrop : function(n, dd, e, data){
56298         var h = data.header;
56299         if(h != n){
56300             var cm = this.grid.colModel;
56301             var x = Roo.lib.Event.getPageX(e);
56302             var r = Roo.lib.Dom.getRegion(n.firstChild);
56303             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56304             var oldIndex = this.view.getCellIndex(h);
56305             var newIndex = this.view.getCellIndex(n);
56306             var locked = cm.isLocked(newIndex);
56307             if(pt == "after"){
56308                 newIndex++;
56309             }
56310             if(oldIndex < newIndex){
56311                 newIndex--;
56312             }
56313             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56314                 return false;
56315             }
56316             cm.setLocked(oldIndex, locked, true);
56317             cm.moveColumn(oldIndex, newIndex);
56318             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56319             return true;
56320         }
56321         return false;
56322     }
56323 });
56324 /*
56325  * Based on:
56326  * Ext JS Library 1.1.1
56327  * Copyright(c) 2006-2007, Ext JS, LLC.
56328  *
56329  * Originally Released Under LGPL - original licence link has changed is not relivant.
56330  *
56331  * Fork - LGPL
56332  * <script type="text/javascript">
56333  */
56334   
56335 /**
56336  * @class Roo.grid.GridView
56337  * @extends Roo.util.Observable
56338  *
56339  * @constructor
56340  * @param {Object} config
56341  */
56342 Roo.grid.GridView = function(config){
56343     Roo.grid.GridView.superclass.constructor.call(this);
56344     this.el = null;
56345
56346     Roo.apply(this, config);
56347 };
56348
56349 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56350
56351     unselectable :  'unselectable="on"',
56352     unselectableCls :  'x-unselectable',
56353     
56354     
56355     rowClass : "x-grid-row",
56356
56357     cellClass : "x-grid-col",
56358
56359     tdClass : "x-grid-td",
56360
56361     hdClass : "x-grid-hd",
56362
56363     splitClass : "x-grid-split",
56364
56365     sortClasses : ["sort-asc", "sort-desc"],
56366
56367     enableMoveAnim : false,
56368
56369     hlColor: "C3DAF9",
56370
56371     dh : Roo.DomHelper,
56372
56373     fly : Roo.Element.fly,
56374
56375     css : Roo.util.CSS,
56376
56377     borderWidth: 1,
56378
56379     splitOffset: 3,
56380
56381     scrollIncrement : 22,
56382
56383     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56384
56385     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56386
56387     bind : function(ds, cm){
56388         if(this.ds){
56389             this.ds.un("load", this.onLoad, this);
56390             this.ds.un("datachanged", this.onDataChange, this);
56391             this.ds.un("add", this.onAdd, this);
56392             this.ds.un("remove", this.onRemove, this);
56393             this.ds.un("update", this.onUpdate, this);
56394             this.ds.un("clear", this.onClear, this);
56395         }
56396         if(ds){
56397             ds.on("load", this.onLoad, this);
56398             ds.on("datachanged", this.onDataChange, this);
56399             ds.on("add", this.onAdd, this);
56400             ds.on("remove", this.onRemove, this);
56401             ds.on("update", this.onUpdate, this);
56402             ds.on("clear", this.onClear, this);
56403         }
56404         this.ds = ds;
56405
56406         if(this.cm){
56407             this.cm.un("widthchange", this.onColWidthChange, this);
56408             this.cm.un("headerchange", this.onHeaderChange, this);
56409             this.cm.un("hiddenchange", this.onHiddenChange, this);
56410             this.cm.un("columnmoved", this.onColumnMove, this);
56411             this.cm.un("columnlockchange", this.onColumnLock, this);
56412         }
56413         if(cm){
56414             this.generateRules(cm);
56415             cm.on("widthchange", this.onColWidthChange, this);
56416             cm.on("headerchange", this.onHeaderChange, this);
56417             cm.on("hiddenchange", this.onHiddenChange, this);
56418             cm.on("columnmoved", this.onColumnMove, this);
56419             cm.on("columnlockchange", this.onColumnLock, this);
56420         }
56421         this.cm = cm;
56422     },
56423
56424     init: function(grid){
56425         Roo.grid.GridView.superclass.init.call(this, grid);
56426
56427         this.bind(grid.dataSource, grid.colModel);
56428
56429         grid.on("headerclick", this.handleHeaderClick, this);
56430
56431         if(grid.trackMouseOver){
56432             grid.on("mouseover", this.onRowOver, this);
56433             grid.on("mouseout", this.onRowOut, this);
56434         }
56435         grid.cancelTextSelection = function(){};
56436         this.gridId = grid.id;
56437
56438         var tpls = this.templates || {};
56439
56440         if(!tpls.master){
56441             tpls.master = new Roo.Template(
56442                '<div class="x-grid" hidefocus="true">',
56443                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56444                   '<div class="x-grid-topbar"></div>',
56445                   '<div class="x-grid-scroller"><div></div></div>',
56446                   '<div class="x-grid-locked">',
56447                       '<div class="x-grid-header">{lockedHeader}</div>',
56448                       '<div class="x-grid-body">{lockedBody}</div>',
56449                   "</div>",
56450                   '<div class="x-grid-viewport">',
56451                       '<div class="x-grid-header">{header}</div>',
56452                       '<div class="x-grid-body">{body}</div>',
56453                   "</div>",
56454                   '<div class="x-grid-bottombar"></div>',
56455                  
56456                   '<div class="x-grid-resize-proxy">&#160;</div>',
56457                "</div>"
56458             );
56459             tpls.master.disableformats = true;
56460         }
56461
56462         if(!tpls.header){
56463             tpls.header = new Roo.Template(
56464                '<table border="0" cellspacing="0" cellpadding="0">',
56465                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56466                "</table>{splits}"
56467             );
56468             tpls.header.disableformats = true;
56469         }
56470         tpls.header.compile();
56471
56472         if(!tpls.hcell){
56473             tpls.hcell = new Roo.Template(
56474                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56475                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56476                 "</div></td>"
56477              );
56478              tpls.hcell.disableFormats = true;
56479         }
56480         tpls.hcell.compile();
56481
56482         if(!tpls.hsplit){
56483             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56484                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56485             tpls.hsplit.disableFormats = true;
56486         }
56487         tpls.hsplit.compile();
56488
56489         if(!tpls.body){
56490             tpls.body = new Roo.Template(
56491                '<table border="0" cellspacing="0" cellpadding="0">',
56492                "<tbody>{rows}</tbody>",
56493                "</table>"
56494             );
56495             tpls.body.disableFormats = true;
56496         }
56497         tpls.body.compile();
56498
56499         if(!tpls.row){
56500             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56501             tpls.row.disableFormats = true;
56502         }
56503         tpls.row.compile();
56504
56505         if(!tpls.cell){
56506             tpls.cell = new Roo.Template(
56507                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56508                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56509                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56510                 "</td>"
56511             );
56512             tpls.cell.disableFormats = true;
56513         }
56514         tpls.cell.compile();
56515
56516         this.templates = tpls;
56517     },
56518
56519     // remap these for backwards compat
56520     onColWidthChange : function(){
56521         this.updateColumns.apply(this, arguments);
56522     },
56523     onHeaderChange : function(){
56524         this.updateHeaders.apply(this, arguments);
56525     }, 
56526     onHiddenChange : function(){
56527         this.handleHiddenChange.apply(this, arguments);
56528     },
56529     onColumnMove : function(){
56530         this.handleColumnMove.apply(this, arguments);
56531     },
56532     onColumnLock : function(){
56533         this.handleLockChange.apply(this, arguments);
56534     },
56535
56536     onDataChange : function(){
56537         this.refresh();
56538         this.updateHeaderSortState();
56539     },
56540
56541     onClear : function(){
56542         this.refresh();
56543     },
56544
56545     onUpdate : function(ds, record){
56546         this.refreshRow(record);
56547     },
56548
56549     refreshRow : function(record){
56550         var ds = this.ds, index;
56551         if(typeof record == 'number'){
56552             index = record;
56553             record = ds.getAt(index);
56554         }else{
56555             index = ds.indexOf(record);
56556         }
56557         this.insertRows(ds, index, index, true);
56558         this.onRemove(ds, record, index+1, true);
56559         this.syncRowHeights(index, index);
56560         this.layout();
56561         this.fireEvent("rowupdated", this, index, record);
56562     },
56563
56564     onAdd : function(ds, records, index){
56565         this.insertRows(ds, index, index + (records.length-1));
56566     },
56567
56568     onRemove : function(ds, record, index, isUpdate){
56569         if(isUpdate !== true){
56570             this.fireEvent("beforerowremoved", this, index, record);
56571         }
56572         var bt = this.getBodyTable(), lt = this.getLockedTable();
56573         if(bt.rows[index]){
56574             bt.firstChild.removeChild(bt.rows[index]);
56575         }
56576         if(lt.rows[index]){
56577             lt.firstChild.removeChild(lt.rows[index]);
56578         }
56579         if(isUpdate !== true){
56580             this.stripeRows(index);
56581             this.syncRowHeights(index, index);
56582             this.layout();
56583             this.fireEvent("rowremoved", this, index, record);
56584         }
56585     },
56586
56587     onLoad : function(){
56588         this.scrollToTop();
56589     },
56590
56591     /**
56592      * Scrolls the grid to the top
56593      */
56594     scrollToTop : function(){
56595         if(this.scroller){
56596             this.scroller.dom.scrollTop = 0;
56597             this.syncScroll();
56598         }
56599     },
56600
56601     /**
56602      * Gets a panel in the header of the grid that can be used for toolbars etc.
56603      * After modifying the contents of this panel a call to grid.autoSize() may be
56604      * required to register any changes in size.
56605      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56606      * @return Roo.Element
56607      */
56608     getHeaderPanel : function(doShow){
56609         if(doShow){
56610             this.headerPanel.show();
56611         }
56612         return this.headerPanel;
56613     },
56614
56615     /**
56616      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56617      * After modifying the contents of this panel a call to grid.autoSize() may be
56618      * required to register any changes in size.
56619      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56620      * @return Roo.Element
56621      */
56622     getFooterPanel : function(doShow){
56623         if(doShow){
56624             this.footerPanel.show();
56625         }
56626         return this.footerPanel;
56627     },
56628
56629     initElements : function(){
56630         var E = Roo.Element;
56631         var el = this.grid.getGridEl().dom.firstChild;
56632         var cs = el.childNodes;
56633
56634         this.el = new E(el);
56635         
56636          this.focusEl = new E(el.firstChild);
56637         this.focusEl.swallowEvent("click", true);
56638         
56639         this.headerPanel = new E(cs[1]);
56640         this.headerPanel.enableDisplayMode("block");
56641
56642         this.scroller = new E(cs[2]);
56643         this.scrollSizer = new E(this.scroller.dom.firstChild);
56644
56645         this.lockedWrap = new E(cs[3]);
56646         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56647         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56648
56649         this.mainWrap = new E(cs[4]);
56650         this.mainHd = new E(this.mainWrap.dom.firstChild);
56651         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56652
56653         this.footerPanel = new E(cs[5]);
56654         this.footerPanel.enableDisplayMode("block");
56655
56656         this.resizeProxy = new E(cs[6]);
56657
56658         this.headerSelector = String.format(
56659            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56660            this.lockedHd.id, this.mainHd.id
56661         );
56662
56663         this.splitterSelector = String.format(
56664            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56665            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56666         );
56667     },
56668     idToCssName : function(s)
56669     {
56670         return s.replace(/[^a-z0-9]+/ig, '-');
56671     },
56672
56673     getHeaderCell : function(index){
56674         return Roo.DomQuery.select(this.headerSelector)[index];
56675     },
56676
56677     getHeaderCellMeasure : function(index){
56678         return this.getHeaderCell(index).firstChild;
56679     },
56680
56681     getHeaderCellText : function(index){
56682         return this.getHeaderCell(index).firstChild.firstChild;
56683     },
56684
56685     getLockedTable : function(){
56686         return this.lockedBody.dom.firstChild;
56687     },
56688
56689     getBodyTable : function(){
56690         return this.mainBody.dom.firstChild;
56691     },
56692
56693     getLockedRow : function(index){
56694         return this.getLockedTable().rows[index];
56695     },
56696
56697     getRow : function(index){
56698         return this.getBodyTable().rows[index];
56699     },
56700
56701     getRowComposite : function(index){
56702         if(!this.rowEl){
56703             this.rowEl = new Roo.CompositeElementLite();
56704         }
56705         var els = [], lrow, mrow;
56706         if(lrow = this.getLockedRow(index)){
56707             els.push(lrow);
56708         }
56709         if(mrow = this.getRow(index)){
56710             els.push(mrow);
56711         }
56712         this.rowEl.elements = els;
56713         return this.rowEl;
56714     },
56715     /**
56716      * Gets the 'td' of the cell
56717      * 
56718      * @param {Integer} rowIndex row to select
56719      * @param {Integer} colIndex column to select
56720      * 
56721      * @return {Object} 
56722      */
56723     getCell : function(rowIndex, colIndex){
56724         var locked = this.cm.getLockedCount();
56725         var source;
56726         if(colIndex < locked){
56727             source = this.lockedBody.dom.firstChild;
56728         }else{
56729             source = this.mainBody.dom.firstChild;
56730             colIndex -= locked;
56731         }
56732         return source.rows[rowIndex].childNodes[colIndex];
56733     },
56734
56735     getCellText : function(rowIndex, colIndex){
56736         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56737     },
56738
56739     getCellBox : function(cell){
56740         var b = this.fly(cell).getBox();
56741         if(Roo.isOpera){ // opera fails to report the Y
56742             b.y = cell.offsetTop + this.mainBody.getY();
56743         }
56744         return b;
56745     },
56746
56747     getCellIndex : function(cell){
56748         var id = String(cell.className).match(this.cellRE);
56749         if(id){
56750             return parseInt(id[1], 10);
56751         }
56752         return 0;
56753     },
56754
56755     findHeaderIndex : function(n){
56756         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56757         return r ? this.getCellIndex(r) : false;
56758     },
56759
56760     findHeaderCell : function(n){
56761         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56762         return r ? r : false;
56763     },
56764
56765     findRowIndex : function(n){
56766         if(!n){
56767             return false;
56768         }
56769         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56770         return r ? r.rowIndex : false;
56771     },
56772
56773     findCellIndex : function(node){
56774         var stop = this.el.dom;
56775         while(node && node != stop){
56776             if(this.findRE.test(node.className)){
56777                 return this.getCellIndex(node);
56778             }
56779             node = node.parentNode;
56780         }
56781         return false;
56782     },
56783
56784     getColumnId : function(index){
56785         return this.cm.getColumnId(index);
56786     },
56787
56788     getSplitters : function()
56789     {
56790         if(this.splitterSelector){
56791            return Roo.DomQuery.select(this.splitterSelector);
56792         }else{
56793             return null;
56794       }
56795     },
56796
56797     getSplitter : function(index){
56798         return this.getSplitters()[index];
56799     },
56800
56801     onRowOver : function(e, t){
56802         var row;
56803         if((row = this.findRowIndex(t)) !== false){
56804             this.getRowComposite(row).addClass("x-grid-row-over");
56805         }
56806     },
56807
56808     onRowOut : function(e, t){
56809         var row;
56810         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56811             this.getRowComposite(row).removeClass("x-grid-row-over");
56812         }
56813     },
56814
56815     renderHeaders : function(){
56816         var cm = this.cm;
56817         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56818         var cb = [], lb = [], sb = [], lsb = [], p = {};
56819         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56820             p.cellId = "x-grid-hd-0-" + i;
56821             p.splitId = "x-grid-csplit-0-" + i;
56822             p.id = cm.getColumnId(i);
56823             p.value = cm.getColumnHeader(i) || "";
56824             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56825             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56826             if(!cm.isLocked(i)){
56827                 cb[cb.length] = ct.apply(p);
56828                 sb[sb.length] = st.apply(p);
56829             }else{
56830                 lb[lb.length] = ct.apply(p);
56831                 lsb[lsb.length] = st.apply(p);
56832             }
56833         }
56834         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56835                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56836     },
56837
56838     updateHeaders : function(){
56839         var html = this.renderHeaders();
56840         this.lockedHd.update(html[0]);
56841         this.mainHd.update(html[1]);
56842     },
56843
56844     /**
56845      * Focuses the specified row.
56846      * @param {Number} row The row index
56847      */
56848     focusRow : function(row)
56849     {
56850         //Roo.log('GridView.focusRow');
56851         var x = this.scroller.dom.scrollLeft;
56852         this.focusCell(row, 0, false);
56853         this.scroller.dom.scrollLeft = x;
56854     },
56855
56856     /**
56857      * Focuses the specified cell.
56858      * @param {Number} row The row index
56859      * @param {Number} col The column index
56860      * @param {Boolean} hscroll false to disable horizontal scrolling
56861      */
56862     focusCell : function(row, col, hscroll)
56863     {
56864         //Roo.log('GridView.focusCell');
56865         var el = this.ensureVisible(row, col, hscroll);
56866         this.focusEl.alignTo(el, "tl-tl");
56867         if(Roo.isGecko){
56868             this.focusEl.focus();
56869         }else{
56870             this.focusEl.focus.defer(1, this.focusEl);
56871         }
56872     },
56873
56874     /**
56875      * Scrolls the specified cell into view
56876      * @param {Number} row The row index
56877      * @param {Number} col The column index
56878      * @param {Boolean} hscroll false to disable horizontal scrolling
56879      */
56880     ensureVisible : function(row, col, hscroll)
56881     {
56882         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56883         //return null; //disable for testing.
56884         if(typeof row != "number"){
56885             row = row.rowIndex;
56886         }
56887         if(row < 0 && row >= this.ds.getCount()){
56888             return  null;
56889         }
56890         col = (col !== undefined ? col : 0);
56891         var cm = this.grid.colModel;
56892         while(cm.isHidden(col)){
56893             col++;
56894         }
56895
56896         var el = this.getCell(row, col);
56897         if(!el){
56898             return null;
56899         }
56900         var c = this.scroller.dom;
56901
56902         var ctop = parseInt(el.offsetTop, 10);
56903         var cleft = parseInt(el.offsetLeft, 10);
56904         var cbot = ctop + el.offsetHeight;
56905         var cright = cleft + el.offsetWidth;
56906         
56907         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56908         var stop = parseInt(c.scrollTop, 10);
56909         var sleft = parseInt(c.scrollLeft, 10);
56910         var sbot = stop + ch;
56911         var sright = sleft + c.clientWidth;
56912         /*
56913         Roo.log('GridView.ensureVisible:' +
56914                 ' ctop:' + ctop +
56915                 ' c.clientHeight:' + c.clientHeight +
56916                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56917                 ' stop:' + stop +
56918                 ' cbot:' + cbot +
56919                 ' sbot:' + sbot +
56920                 ' ch:' + ch  
56921                 );
56922         */
56923         if(ctop < stop){
56924             c.scrollTop = ctop;
56925             //Roo.log("set scrolltop to ctop DISABLE?");
56926         }else if(cbot > sbot){
56927             //Roo.log("set scrolltop to cbot-ch");
56928             c.scrollTop = cbot-ch;
56929         }
56930         
56931         if(hscroll !== false){
56932             if(cleft < sleft){
56933                 c.scrollLeft = cleft;
56934             }else if(cright > sright){
56935                 c.scrollLeft = cright-c.clientWidth;
56936             }
56937         }
56938          
56939         return el;
56940     },
56941
56942     updateColumns : function(){
56943         this.grid.stopEditing();
56944         var cm = this.grid.colModel, colIds = this.getColumnIds();
56945         //var totalWidth = cm.getTotalWidth();
56946         var pos = 0;
56947         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56948             //if(cm.isHidden(i)) continue;
56949             var w = cm.getColumnWidth(i);
56950             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56951             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56952         }
56953         this.updateSplitters();
56954     },
56955
56956     generateRules : function(cm){
56957         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56958         Roo.util.CSS.removeStyleSheet(rulesId);
56959         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56960             var cid = cm.getColumnId(i);
56961             var align = '';
56962             if(cm.config[i].align){
56963                 align = 'text-align:'+cm.config[i].align+';';
56964             }
56965             var hidden = '';
56966             if(cm.isHidden(i)){
56967                 hidden = 'display:none;';
56968             }
56969             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56970             ruleBuf.push(
56971                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56972                     this.hdSelector, cid, " {\n", align, width, "}\n",
56973                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56974                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56975         }
56976         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56977     },
56978
56979     updateSplitters : function(){
56980         var cm = this.cm, s = this.getSplitters();
56981         if(s){ // splitters not created yet
56982             var pos = 0, locked = true;
56983             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56984                 if(cm.isHidden(i)) {
56985                     continue;
56986                 }
56987                 var w = cm.getColumnWidth(i); // make sure it's a number
56988                 if(!cm.isLocked(i) && locked){
56989                     pos = 0;
56990                     locked = false;
56991                 }
56992                 pos += w;
56993                 s[i].style.left = (pos-this.splitOffset) + "px";
56994             }
56995         }
56996     },
56997
56998     handleHiddenChange : function(colModel, colIndex, hidden){
56999         if(hidden){
57000             this.hideColumn(colIndex);
57001         }else{
57002             this.unhideColumn(colIndex);
57003         }
57004     },
57005
57006     hideColumn : function(colIndex){
57007         var cid = this.getColumnId(colIndex);
57008         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57009         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57010         if(Roo.isSafari){
57011             this.updateHeaders();
57012         }
57013         this.updateSplitters();
57014         this.layout();
57015     },
57016
57017     unhideColumn : function(colIndex){
57018         var cid = this.getColumnId(colIndex);
57019         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57020         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57021
57022         if(Roo.isSafari){
57023             this.updateHeaders();
57024         }
57025         this.updateSplitters();
57026         this.layout();
57027     },
57028
57029     insertRows : function(dm, firstRow, lastRow, isUpdate){
57030         if(firstRow == 0 && lastRow == dm.getCount()-1){
57031             this.refresh();
57032         }else{
57033             if(!isUpdate){
57034                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57035             }
57036             var s = this.getScrollState();
57037             var markup = this.renderRows(firstRow, lastRow);
57038             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57039             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57040             this.restoreScroll(s);
57041             if(!isUpdate){
57042                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57043                 this.syncRowHeights(firstRow, lastRow);
57044                 this.stripeRows(firstRow);
57045                 this.layout();
57046             }
57047         }
57048     },
57049
57050     bufferRows : function(markup, target, index){
57051         var before = null, trows = target.rows, tbody = target.tBodies[0];
57052         if(index < trows.length){
57053             before = trows[index];
57054         }
57055         var b = document.createElement("div");
57056         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57057         var rows = b.firstChild.rows;
57058         for(var i = 0, len = rows.length; i < len; i++){
57059             if(before){
57060                 tbody.insertBefore(rows[0], before);
57061             }else{
57062                 tbody.appendChild(rows[0]);
57063             }
57064         }
57065         b.innerHTML = "";
57066         b = null;
57067     },
57068
57069     deleteRows : function(dm, firstRow, lastRow){
57070         if(dm.getRowCount()<1){
57071             this.fireEvent("beforerefresh", this);
57072             this.mainBody.update("");
57073             this.lockedBody.update("");
57074             this.fireEvent("refresh", this);
57075         }else{
57076             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57077             var bt = this.getBodyTable();
57078             var tbody = bt.firstChild;
57079             var rows = bt.rows;
57080             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57081                 tbody.removeChild(rows[firstRow]);
57082             }
57083             this.stripeRows(firstRow);
57084             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57085         }
57086     },
57087
57088     updateRows : function(dataSource, firstRow, lastRow){
57089         var s = this.getScrollState();
57090         this.refresh();
57091         this.restoreScroll(s);
57092     },
57093
57094     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57095         if(!noRefresh){
57096            this.refresh();
57097         }
57098         this.updateHeaderSortState();
57099     },
57100
57101     getScrollState : function(){
57102         
57103         var sb = this.scroller.dom;
57104         return {left: sb.scrollLeft, top: sb.scrollTop};
57105     },
57106
57107     stripeRows : function(startRow){
57108         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57109             return;
57110         }
57111         startRow = startRow || 0;
57112         var rows = this.getBodyTable().rows;
57113         var lrows = this.getLockedTable().rows;
57114         var cls = ' x-grid-row-alt ';
57115         for(var i = startRow, len = rows.length; i < len; i++){
57116             var row = rows[i], lrow = lrows[i];
57117             var isAlt = ((i+1) % 2 == 0);
57118             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57119             if(isAlt == hasAlt){
57120                 continue;
57121             }
57122             if(isAlt){
57123                 row.className += " x-grid-row-alt";
57124             }else{
57125                 row.className = row.className.replace("x-grid-row-alt", "");
57126             }
57127             if(lrow){
57128                 lrow.className = row.className;
57129             }
57130         }
57131     },
57132
57133     restoreScroll : function(state){
57134         //Roo.log('GridView.restoreScroll');
57135         var sb = this.scroller.dom;
57136         sb.scrollLeft = state.left;
57137         sb.scrollTop = state.top;
57138         this.syncScroll();
57139     },
57140
57141     syncScroll : function(){
57142         //Roo.log('GridView.syncScroll');
57143         var sb = this.scroller.dom;
57144         var sh = this.mainHd.dom;
57145         var bs = this.mainBody.dom;
57146         var lv = this.lockedBody.dom;
57147         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57148         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57149     },
57150
57151     handleScroll : function(e){
57152         this.syncScroll();
57153         var sb = this.scroller.dom;
57154         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57155         e.stopEvent();
57156     },
57157
57158     handleWheel : function(e){
57159         var d = e.getWheelDelta();
57160         this.scroller.dom.scrollTop -= d*22;
57161         // set this here to prevent jumpy scrolling on large tables
57162         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57163         e.stopEvent();
57164     },
57165
57166     renderRows : function(startRow, endRow){
57167         // pull in all the crap needed to render rows
57168         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57169         var colCount = cm.getColumnCount();
57170
57171         if(ds.getCount() < 1){
57172             return ["", ""];
57173         }
57174
57175         // build a map for all the columns
57176         var cs = [];
57177         for(var i = 0; i < colCount; i++){
57178             var name = cm.getDataIndex(i);
57179             cs[i] = {
57180                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57181                 renderer : cm.getRenderer(i),
57182                 id : cm.getColumnId(i),
57183                 locked : cm.isLocked(i),
57184                 has_editor : cm.isCellEditable(i)
57185             };
57186         }
57187
57188         startRow = startRow || 0;
57189         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57190
57191         // records to render
57192         var rs = ds.getRange(startRow, endRow);
57193
57194         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57195     },
57196
57197     // As much as I hate to duplicate code, this was branched because FireFox really hates
57198     // [].join("") on strings. The performance difference was substantial enough to
57199     // branch this function
57200     doRender : Roo.isGecko ?
57201             function(cs, rs, ds, startRow, colCount, stripe){
57202                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57203                 // buffers
57204                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57205                 
57206                 var hasListener = this.grid.hasListener('rowclass');
57207                 var rowcfg = {};
57208                 for(var j = 0, len = rs.length; j < len; j++){
57209                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57210                     for(var i = 0; i < colCount; i++){
57211                         c = cs[i];
57212                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57213                         p.id = c.id;
57214                         p.css = p.attr = "";
57215                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57216                         if(p.value == undefined || p.value === "") {
57217                             p.value = "&#160;";
57218                         }
57219                         if(c.has_editor){
57220                             p.css += ' x-grid-editable-cell';
57221                         }
57222                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57223                             p.css +=  ' x-grid-dirty-cell';
57224                         }
57225                         var markup = ct.apply(p);
57226                         if(!c.locked){
57227                             cb+= markup;
57228                         }else{
57229                             lcb+= markup;
57230                         }
57231                     }
57232                     var alt = [];
57233                     if(stripe && ((rowIndex+1) % 2 == 0)){
57234                         alt.push("x-grid-row-alt")
57235                     }
57236                     if(r.dirty){
57237                         alt.push(  " x-grid-dirty-row");
57238                     }
57239                     rp.cells = lcb;
57240                     if(this.getRowClass){
57241                         alt.push(this.getRowClass(r, rowIndex));
57242                     }
57243                     if (hasListener) {
57244                         rowcfg = {
57245                              
57246                             record: r,
57247                             rowIndex : rowIndex,
57248                             rowClass : ''
57249                         };
57250                         this.grid.fireEvent('rowclass', this, rowcfg);
57251                         alt.push(rowcfg.rowClass);
57252                     }
57253                     rp.alt = alt.join(" ");
57254                     lbuf+= rt.apply(rp);
57255                     rp.cells = cb;
57256                     buf+=  rt.apply(rp);
57257                 }
57258                 return [lbuf, buf];
57259             } :
57260             function(cs, rs, ds, startRow, colCount, stripe){
57261                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57262                 // buffers
57263                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57264                 var hasListener = this.grid.hasListener('rowclass');
57265  
57266                 var rowcfg = {};
57267                 for(var j = 0, len = rs.length; j < len; j++){
57268                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57269                     for(var i = 0; i < colCount; i++){
57270                         c = cs[i];
57271                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57272                         p.id = c.id;
57273                         p.css = p.attr = "";
57274                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57275                         if(p.value == undefined || p.value === "") {
57276                             p.value = "&#160;";
57277                         }
57278                         //Roo.log(c);
57279                          if(c.has_editor){
57280                             p.css += ' x-grid-editable-cell';
57281                         }
57282                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57283                             p.css += ' x-grid-dirty-cell' 
57284                         }
57285                         
57286                         var markup = ct.apply(p);
57287                         if(!c.locked){
57288                             cb[cb.length] = markup;
57289                         }else{
57290                             lcb[lcb.length] = markup;
57291                         }
57292                     }
57293                     var alt = [];
57294                     if(stripe && ((rowIndex+1) % 2 == 0)){
57295                         alt.push( "x-grid-row-alt");
57296                     }
57297                     if(r.dirty){
57298                         alt.push(" x-grid-dirty-row");
57299                     }
57300                     rp.cells = lcb;
57301                     if(this.getRowClass){
57302                         alt.push( this.getRowClass(r, rowIndex));
57303                     }
57304                     if (hasListener) {
57305                         rowcfg = {
57306                              
57307                             record: r,
57308                             rowIndex : rowIndex,
57309                             rowClass : ''
57310                         };
57311                         this.grid.fireEvent('rowclass', this, rowcfg);
57312                         alt.push(rowcfg.rowClass);
57313                     }
57314                     
57315                     rp.alt = alt.join(" ");
57316                     rp.cells = lcb.join("");
57317                     lbuf[lbuf.length] = rt.apply(rp);
57318                     rp.cells = cb.join("");
57319                     buf[buf.length] =  rt.apply(rp);
57320                 }
57321                 return [lbuf.join(""), buf.join("")];
57322             },
57323
57324     renderBody : function(){
57325         var markup = this.renderRows();
57326         var bt = this.templates.body;
57327         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57328     },
57329
57330     /**
57331      * Refreshes the grid
57332      * @param {Boolean} headersToo
57333      */
57334     refresh : function(headersToo){
57335         this.fireEvent("beforerefresh", this);
57336         this.grid.stopEditing();
57337         var result = this.renderBody();
57338         this.lockedBody.update(result[0]);
57339         this.mainBody.update(result[1]);
57340         if(headersToo === true){
57341             this.updateHeaders();
57342             this.updateColumns();
57343             this.updateSplitters();
57344             this.updateHeaderSortState();
57345         }
57346         this.syncRowHeights();
57347         this.layout();
57348         this.fireEvent("refresh", this);
57349     },
57350
57351     handleColumnMove : function(cm, oldIndex, newIndex){
57352         this.indexMap = null;
57353         var s = this.getScrollState();
57354         this.refresh(true);
57355         this.restoreScroll(s);
57356         this.afterMove(newIndex);
57357     },
57358
57359     afterMove : function(colIndex){
57360         if(this.enableMoveAnim && Roo.enableFx){
57361             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57362         }
57363         // if multisort - fix sortOrder, and reload..
57364         if (this.grid.dataSource.multiSort) {
57365             // the we can call sort again..
57366             var dm = this.grid.dataSource;
57367             var cm = this.grid.colModel;
57368             var so = [];
57369             for(var i = 0; i < cm.config.length; i++ ) {
57370                 
57371                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57372                     continue; // dont' bother, it's not in sort list or being set.
57373                 }
57374                 
57375                 so.push(cm.config[i].dataIndex);
57376             };
57377             dm.sortOrder = so;
57378             dm.load(dm.lastOptions);
57379             
57380             
57381         }
57382         
57383     },
57384
57385     updateCell : function(dm, rowIndex, dataIndex){
57386         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57387         if(typeof colIndex == "undefined"){ // not present in grid
57388             return;
57389         }
57390         var cm = this.grid.colModel;
57391         var cell = this.getCell(rowIndex, colIndex);
57392         var cellText = this.getCellText(rowIndex, colIndex);
57393
57394         var p = {
57395             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57396             id : cm.getColumnId(colIndex),
57397             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57398         };
57399         var renderer = cm.getRenderer(colIndex);
57400         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57401         if(typeof val == "undefined" || val === "") {
57402             val = "&#160;";
57403         }
57404         cellText.innerHTML = val;
57405         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57406         this.syncRowHeights(rowIndex, rowIndex);
57407     },
57408
57409     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57410         var maxWidth = 0;
57411         if(this.grid.autoSizeHeaders){
57412             var h = this.getHeaderCellMeasure(colIndex);
57413             maxWidth = Math.max(maxWidth, h.scrollWidth);
57414         }
57415         var tb, index;
57416         if(this.cm.isLocked(colIndex)){
57417             tb = this.getLockedTable();
57418             index = colIndex;
57419         }else{
57420             tb = this.getBodyTable();
57421             index = colIndex - this.cm.getLockedCount();
57422         }
57423         if(tb && tb.rows){
57424             var rows = tb.rows;
57425             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57426             for(var i = 0; i < stopIndex; i++){
57427                 var cell = rows[i].childNodes[index].firstChild;
57428                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57429             }
57430         }
57431         return maxWidth + /*margin for error in IE*/ 5;
57432     },
57433     /**
57434      * Autofit a column to its content.
57435      * @param {Number} colIndex
57436      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57437      */
57438      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57439          if(this.cm.isHidden(colIndex)){
57440              return; // can't calc a hidden column
57441          }
57442         if(forceMinSize){
57443             var cid = this.cm.getColumnId(colIndex);
57444             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57445            if(this.grid.autoSizeHeaders){
57446                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57447            }
57448         }
57449         var newWidth = this.calcColumnWidth(colIndex);
57450         this.cm.setColumnWidth(colIndex,
57451             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57452         if(!suppressEvent){
57453             this.grid.fireEvent("columnresize", colIndex, newWidth);
57454         }
57455     },
57456
57457     /**
57458      * Autofits all columns to their content and then expands to fit any extra space in the grid
57459      */
57460      autoSizeColumns : function(){
57461         var cm = this.grid.colModel;
57462         var colCount = cm.getColumnCount();
57463         for(var i = 0; i < colCount; i++){
57464             this.autoSizeColumn(i, true, true);
57465         }
57466         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57467             this.fitColumns();
57468         }else{
57469             this.updateColumns();
57470             this.layout();
57471         }
57472     },
57473
57474     /**
57475      * Autofits all columns to the grid's width proportionate with their current size
57476      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57477      */
57478     fitColumns : function(reserveScrollSpace){
57479         var cm = this.grid.colModel;
57480         var colCount = cm.getColumnCount();
57481         var cols = [];
57482         var width = 0;
57483         var i, w;
57484         for (i = 0; i < colCount; i++){
57485             if(!cm.isHidden(i) && !cm.isFixed(i)){
57486                 w = cm.getColumnWidth(i);
57487                 cols.push(i);
57488                 cols.push(w);
57489                 width += w;
57490             }
57491         }
57492         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57493         if(reserveScrollSpace){
57494             avail -= 17;
57495         }
57496         var frac = (avail - cm.getTotalWidth())/width;
57497         while (cols.length){
57498             w = cols.pop();
57499             i = cols.pop();
57500             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57501         }
57502         this.updateColumns();
57503         this.layout();
57504     },
57505
57506     onRowSelect : function(rowIndex){
57507         var row = this.getRowComposite(rowIndex);
57508         row.addClass("x-grid-row-selected");
57509     },
57510
57511     onRowDeselect : function(rowIndex){
57512         var row = this.getRowComposite(rowIndex);
57513         row.removeClass("x-grid-row-selected");
57514     },
57515
57516     onCellSelect : function(row, col){
57517         var cell = this.getCell(row, col);
57518         if(cell){
57519             Roo.fly(cell).addClass("x-grid-cell-selected");
57520         }
57521     },
57522
57523     onCellDeselect : function(row, col){
57524         var cell = this.getCell(row, col);
57525         if(cell){
57526             Roo.fly(cell).removeClass("x-grid-cell-selected");
57527         }
57528     },
57529
57530     updateHeaderSortState : function(){
57531         
57532         // sort state can be single { field: xxx, direction : yyy}
57533         // or   { xxx=>ASC , yyy : DESC ..... }
57534         
57535         var mstate = {};
57536         if (!this.ds.multiSort) { 
57537             var state = this.ds.getSortState();
57538             if(!state){
57539                 return;
57540             }
57541             mstate[state.field] = state.direction;
57542             // FIXME... - this is not used here.. but might be elsewhere..
57543             this.sortState = state;
57544             
57545         } else {
57546             mstate = this.ds.sortToggle;
57547         }
57548         //remove existing sort classes..
57549         
57550         var sc = this.sortClasses;
57551         var hds = this.el.select(this.headerSelector).removeClass(sc);
57552         
57553         for(var f in mstate) {
57554         
57555             var sortColumn = this.cm.findColumnIndex(f);
57556             
57557             if(sortColumn != -1){
57558                 var sortDir = mstate[f];        
57559                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57560             }
57561         }
57562         
57563          
57564         
57565     },
57566
57567
57568     handleHeaderClick : function(g, index,e){
57569         
57570         Roo.log("header click");
57571         
57572         if (Roo.isTouch) {
57573             // touch events on header are handled by context
57574             this.handleHdCtx(g,index,e);
57575             return;
57576         }
57577         
57578         
57579         if(this.headersDisabled){
57580             return;
57581         }
57582         var dm = g.dataSource, cm = g.colModel;
57583         if(!cm.isSortable(index)){
57584             return;
57585         }
57586         g.stopEditing();
57587         
57588         if (dm.multiSort) {
57589             // update the sortOrder
57590             var so = [];
57591             for(var i = 0; i < cm.config.length; i++ ) {
57592                 
57593                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57594                     continue; // dont' bother, it's not in sort list or being set.
57595                 }
57596                 
57597                 so.push(cm.config[i].dataIndex);
57598             };
57599             dm.sortOrder = so;
57600         }
57601         
57602         
57603         dm.sort(cm.getDataIndex(index));
57604     },
57605
57606
57607     destroy : function(){
57608         if(this.colMenu){
57609             this.colMenu.removeAll();
57610             Roo.menu.MenuMgr.unregister(this.colMenu);
57611             this.colMenu.getEl().remove();
57612             delete this.colMenu;
57613         }
57614         if(this.hmenu){
57615             this.hmenu.removeAll();
57616             Roo.menu.MenuMgr.unregister(this.hmenu);
57617             this.hmenu.getEl().remove();
57618             delete this.hmenu;
57619         }
57620         if(this.grid.enableColumnMove){
57621             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57622             if(dds){
57623                 for(var dd in dds){
57624                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57625                         var elid = dds[dd].dragElId;
57626                         dds[dd].unreg();
57627                         Roo.get(elid).remove();
57628                     } else if(dds[dd].config.isTarget){
57629                         dds[dd].proxyTop.remove();
57630                         dds[dd].proxyBottom.remove();
57631                         dds[dd].unreg();
57632                     }
57633                     if(Roo.dd.DDM.locationCache[dd]){
57634                         delete Roo.dd.DDM.locationCache[dd];
57635                     }
57636                 }
57637                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57638             }
57639         }
57640         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57641         this.bind(null, null);
57642         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57643     },
57644
57645     handleLockChange : function(){
57646         this.refresh(true);
57647     },
57648
57649     onDenyColumnLock : function(){
57650
57651     },
57652
57653     onDenyColumnHide : function(){
57654
57655     },
57656
57657     handleHdMenuClick : function(item){
57658         var index = this.hdCtxIndex;
57659         var cm = this.cm, ds = this.ds;
57660         switch(item.id){
57661             case "asc":
57662                 ds.sort(cm.getDataIndex(index), "ASC");
57663                 break;
57664             case "desc":
57665                 ds.sort(cm.getDataIndex(index), "DESC");
57666                 break;
57667             case "lock":
57668                 var lc = cm.getLockedCount();
57669                 if(cm.getColumnCount(true) <= lc+1){
57670                     this.onDenyColumnLock();
57671                     return;
57672                 }
57673                 if(lc != index){
57674                     cm.setLocked(index, true, true);
57675                     cm.moveColumn(index, lc);
57676                     this.grid.fireEvent("columnmove", index, lc);
57677                 }else{
57678                     cm.setLocked(index, true);
57679                 }
57680             break;
57681             case "unlock":
57682                 var lc = cm.getLockedCount();
57683                 if((lc-1) != index){
57684                     cm.setLocked(index, false, true);
57685                     cm.moveColumn(index, lc-1);
57686                     this.grid.fireEvent("columnmove", index, lc-1);
57687                 }else{
57688                     cm.setLocked(index, false);
57689                 }
57690             break;
57691             case 'wider': // used to expand cols on touch..
57692             case 'narrow':
57693                 var cw = cm.getColumnWidth(index);
57694                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57695                 cw = Math.max(0, cw);
57696                 cw = Math.min(cw,4000);
57697                 cm.setColumnWidth(index, cw);
57698                 break;
57699                 
57700             default:
57701                 index = cm.getIndexById(item.id.substr(4));
57702                 if(index != -1){
57703                     if(item.checked && cm.getColumnCount(true) <= 1){
57704                         this.onDenyColumnHide();
57705                         return false;
57706                     }
57707                     cm.setHidden(index, item.checked);
57708                 }
57709         }
57710         return true;
57711     },
57712
57713     beforeColMenuShow : function(){
57714         var cm = this.cm,  colCount = cm.getColumnCount();
57715         this.colMenu.removeAll();
57716         for(var i = 0; i < colCount; i++){
57717             this.colMenu.add(new Roo.menu.CheckItem({
57718                 id: "col-"+cm.getColumnId(i),
57719                 text: cm.getColumnHeader(i),
57720                 checked: !cm.isHidden(i),
57721                 hideOnClick:false
57722             }));
57723         }
57724     },
57725
57726     handleHdCtx : function(g, index, e){
57727         e.stopEvent();
57728         var hd = this.getHeaderCell(index);
57729         this.hdCtxIndex = index;
57730         var ms = this.hmenu.items, cm = this.cm;
57731         ms.get("asc").setDisabled(!cm.isSortable(index));
57732         ms.get("desc").setDisabled(!cm.isSortable(index));
57733         if(this.grid.enableColLock !== false){
57734             ms.get("lock").setDisabled(cm.isLocked(index));
57735             ms.get("unlock").setDisabled(!cm.isLocked(index));
57736         }
57737         this.hmenu.show(hd, "tl-bl");
57738     },
57739
57740     handleHdOver : function(e){
57741         var hd = this.findHeaderCell(e.getTarget());
57742         if(hd && !this.headersDisabled){
57743             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57744                this.fly(hd).addClass("x-grid-hd-over");
57745             }
57746         }
57747     },
57748
57749     handleHdOut : function(e){
57750         var hd = this.findHeaderCell(e.getTarget());
57751         if(hd){
57752             this.fly(hd).removeClass("x-grid-hd-over");
57753         }
57754     },
57755
57756     handleSplitDblClick : function(e, t){
57757         var i = this.getCellIndex(t);
57758         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57759             this.autoSizeColumn(i, true);
57760             this.layout();
57761         }
57762     },
57763
57764     render : function(){
57765
57766         var cm = this.cm;
57767         var colCount = cm.getColumnCount();
57768
57769         if(this.grid.monitorWindowResize === true){
57770             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57771         }
57772         var header = this.renderHeaders();
57773         var body = this.templates.body.apply({rows:""});
57774         var html = this.templates.master.apply({
57775             lockedBody: body,
57776             body: body,
57777             lockedHeader: header[0],
57778             header: header[1]
57779         });
57780
57781         //this.updateColumns();
57782
57783         this.grid.getGridEl().dom.innerHTML = html;
57784
57785         this.initElements();
57786         
57787         // a kludge to fix the random scolling effect in webkit
57788         this.el.on("scroll", function() {
57789             this.el.dom.scrollTop=0; // hopefully not recursive..
57790         },this);
57791
57792         this.scroller.on("scroll", this.handleScroll, this);
57793         this.lockedBody.on("mousewheel", this.handleWheel, this);
57794         this.mainBody.on("mousewheel", this.handleWheel, this);
57795
57796         this.mainHd.on("mouseover", this.handleHdOver, this);
57797         this.mainHd.on("mouseout", this.handleHdOut, this);
57798         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57799                 {delegate: "."+this.splitClass});
57800
57801         this.lockedHd.on("mouseover", this.handleHdOver, this);
57802         this.lockedHd.on("mouseout", this.handleHdOut, this);
57803         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57804                 {delegate: "."+this.splitClass});
57805
57806         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57807             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57808         }
57809
57810         this.updateSplitters();
57811
57812         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57813             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57814             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57815         }
57816
57817         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57818             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57819             this.hmenu.add(
57820                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57821                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57822             );
57823             if(this.grid.enableColLock !== false){
57824                 this.hmenu.add('-',
57825                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57826                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57827                 );
57828             }
57829             if (Roo.isTouch) {
57830                  this.hmenu.add('-',
57831                     {id:"wider", text: this.columnsWiderText},
57832                     {id:"narrow", text: this.columnsNarrowText }
57833                 );
57834                 
57835                  
57836             }
57837             
57838             if(this.grid.enableColumnHide !== false){
57839
57840                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57841                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57842                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57843
57844                 this.hmenu.add('-',
57845                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57846                 );
57847             }
57848             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57849
57850             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57851         }
57852
57853         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57854             this.dd = new Roo.grid.GridDragZone(this.grid, {
57855                 ddGroup : this.grid.ddGroup || 'GridDD'
57856             });
57857             
57858         }
57859
57860         /*
57861         for(var i = 0; i < colCount; i++){
57862             if(cm.isHidden(i)){
57863                 this.hideColumn(i);
57864             }
57865             if(cm.config[i].align){
57866                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57867                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57868             }
57869         }*/
57870         
57871         this.updateHeaderSortState();
57872
57873         this.beforeInitialResize();
57874         this.layout(true);
57875
57876         // two part rendering gives faster view to the user
57877         this.renderPhase2.defer(1, this);
57878     },
57879
57880     renderPhase2 : function(){
57881         // render the rows now
57882         this.refresh();
57883         if(this.grid.autoSizeColumns){
57884             this.autoSizeColumns();
57885         }
57886     },
57887
57888     beforeInitialResize : function(){
57889
57890     },
57891
57892     onColumnSplitterMoved : function(i, w){
57893         this.userResized = true;
57894         var cm = this.grid.colModel;
57895         cm.setColumnWidth(i, w, true);
57896         var cid = cm.getColumnId(i);
57897         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57898         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57899         this.updateSplitters();
57900         this.layout();
57901         this.grid.fireEvent("columnresize", i, w);
57902     },
57903
57904     syncRowHeights : function(startIndex, endIndex){
57905         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57906             startIndex = startIndex || 0;
57907             var mrows = this.getBodyTable().rows;
57908             var lrows = this.getLockedTable().rows;
57909             var len = mrows.length-1;
57910             endIndex = Math.min(endIndex || len, len);
57911             for(var i = startIndex; i <= endIndex; i++){
57912                 var m = mrows[i], l = lrows[i];
57913                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57914                 m.style.height = l.style.height = h + "px";
57915             }
57916         }
57917     },
57918
57919     layout : function(initialRender, is2ndPass)
57920     {
57921         var g = this.grid;
57922         var auto = g.autoHeight;
57923         var scrollOffset = 16;
57924         var c = g.getGridEl(), cm = this.cm,
57925                 expandCol = g.autoExpandColumn,
57926                 gv = this;
57927         //c.beginMeasure();
57928
57929         if(!c.dom.offsetWidth){ // display:none?
57930             if(initialRender){
57931                 this.lockedWrap.show();
57932                 this.mainWrap.show();
57933             }
57934             return;
57935         }
57936
57937         var hasLock = this.cm.isLocked(0);
57938
57939         var tbh = this.headerPanel.getHeight();
57940         var bbh = this.footerPanel.getHeight();
57941
57942         if(auto){
57943             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57944             var newHeight = ch + c.getBorderWidth("tb");
57945             if(g.maxHeight){
57946                 newHeight = Math.min(g.maxHeight, newHeight);
57947             }
57948             c.setHeight(newHeight);
57949         }
57950
57951         if(g.autoWidth){
57952             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57953         }
57954
57955         var s = this.scroller;
57956
57957         var csize = c.getSize(true);
57958
57959         this.el.setSize(csize.width, csize.height);
57960
57961         this.headerPanel.setWidth(csize.width);
57962         this.footerPanel.setWidth(csize.width);
57963
57964         var hdHeight = this.mainHd.getHeight();
57965         var vw = csize.width;
57966         var vh = csize.height - (tbh + bbh);
57967
57968         s.setSize(vw, vh);
57969
57970         var bt = this.getBodyTable();
57971         
57972         if(cm.getLockedCount() == cm.config.length){
57973             bt = this.getLockedTable();
57974         }
57975         
57976         var ltWidth = hasLock ?
57977                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57978
57979         var scrollHeight = bt.offsetHeight;
57980         var scrollWidth = ltWidth + bt.offsetWidth;
57981         var vscroll = false, hscroll = false;
57982
57983         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57984
57985         var lw = this.lockedWrap, mw = this.mainWrap;
57986         var lb = this.lockedBody, mb = this.mainBody;
57987
57988         setTimeout(function(){
57989             var t = s.dom.offsetTop;
57990             var w = s.dom.clientWidth,
57991                 h = s.dom.clientHeight;
57992
57993             lw.setTop(t);
57994             lw.setSize(ltWidth, h);
57995
57996             mw.setLeftTop(ltWidth, t);
57997             mw.setSize(w-ltWidth, h);
57998
57999             lb.setHeight(h-hdHeight);
58000             mb.setHeight(h-hdHeight);
58001
58002             if(is2ndPass !== true && !gv.userResized && expandCol){
58003                 // high speed resize without full column calculation
58004                 
58005                 var ci = cm.getIndexById(expandCol);
58006                 if (ci < 0) {
58007                     ci = cm.findColumnIndex(expandCol);
58008                 }
58009                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58010                 var expandId = cm.getColumnId(ci);
58011                 var  tw = cm.getTotalWidth(false);
58012                 var currentWidth = cm.getColumnWidth(ci);
58013                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58014                 if(currentWidth != cw){
58015                     cm.setColumnWidth(ci, cw, true);
58016                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58017                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58018                     gv.updateSplitters();
58019                     gv.layout(false, true);
58020                 }
58021             }
58022
58023             if(initialRender){
58024                 lw.show();
58025                 mw.show();
58026             }
58027             //c.endMeasure();
58028         }, 10);
58029     },
58030
58031     onWindowResize : function(){
58032         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58033             return;
58034         }
58035         this.layout();
58036     },
58037
58038     appendFooter : function(parentEl){
58039         return null;
58040     },
58041
58042     sortAscText : "Sort Ascending",
58043     sortDescText : "Sort Descending",
58044     lockText : "Lock Column",
58045     unlockText : "Unlock Column",
58046     columnsText : "Columns",
58047  
58048     columnsWiderText : "Wider",
58049     columnsNarrowText : "Thinner"
58050 });
58051
58052
58053 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58054     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58055     this.proxy.el.addClass('x-grid3-col-dd');
58056 };
58057
58058 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58059     handleMouseDown : function(e){
58060
58061     },
58062
58063     callHandleMouseDown : function(e){
58064         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58065     }
58066 });
58067 /*
58068  * Based on:
58069  * Ext JS Library 1.1.1
58070  * Copyright(c) 2006-2007, Ext JS, LLC.
58071  *
58072  * Originally Released Under LGPL - original licence link has changed is not relivant.
58073  *
58074  * Fork - LGPL
58075  * <script type="text/javascript">
58076  */
58077  /**
58078  * @extends Roo.dd.DDProxy
58079  * @class Roo.grid.SplitDragZone
58080  * Support for Column Header resizing
58081  * @constructor
58082  * @param {Object} config
58083  */
58084 // private
58085 // This is a support class used internally by the Grid components
58086 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58087     this.grid = grid;
58088     this.view = grid.getView();
58089     this.proxy = this.view.resizeProxy;
58090     Roo.grid.SplitDragZone.superclass.constructor.call(
58091         this,
58092         hd, // ID
58093         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58094         {  // CONFIG
58095             dragElId : Roo.id(this.proxy.dom),
58096             resizeFrame:false
58097         }
58098     );
58099     
58100     this.setHandleElId(Roo.id(hd));
58101     if (hd2 !== false) {
58102         this.setOuterHandleElId(Roo.id(hd2));
58103     }
58104     
58105     this.scroll = false;
58106 };
58107 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58108     fly: Roo.Element.fly,
58109
58110     b4StartDrag : function(x, y){
58111         this.view.headersDisabled = true;
58112         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58113                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58114         );
58115         this.proxy.setHeight(h);
58116         
58117         // for old system colWidth really stored the actual width?
58118         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58119         // which in reality did not work.. - it worked only for fixed sizes
58120         // for resizable we need to use actual sizes.
58121         var w = this.cm.getColumnWidth(this.cellIndex);
58122         if (!this.view.mainWrap) {
58123             // bootstrap.
58124             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58125         }
58126         
58127         
58128         
58129         // this was w-this.grid.minColumnWidth;
58130         // doesnt really make sense? - w = thie curren width or the rendered one?
58131         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58132         this.resetConstraints();
58133         this.setXConstraint(minw, 1000);
58134         this.setYConstraint(0, 0);
58135         this.minX = x - minw;
58136         this.maxX = x + 1000;
58137         this.startPos = x;
58138         if (!this.view.mainWrap) { // this is Bootstrap code..
58139             this.getDragEl().style.display='block';
58140         }
58141         
58142         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58143     },
58144
58145
58146     handleMouseDown : function(e){
58147         ev = Roo.EventObject.setEvent(e);
58148         var t = this.fly(ev.getTarget());
58149         if(t.hasClass("x-grid-split")){
58150             this.cellIndex = this.view.getCellIndex(t.dom);
58151             this.split = t.dom;
58152             this.cm = this.grid.colModel;
58153             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58154                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58155             }
58156         }
58157     },
58158
58159     endDrag : function(e){
58160         this.view.headersDisabled = false;
58161         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58162         var diff = endX - this.startPos;
58163         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58164     },
58165
58166     autoOffset : function(){
58167         this.setDelta(0,0);
58168     }
58169 });/*
58170  * Based on:
58171  * Ext JS Library 1.1.1
58172  * Copyright(c) 2006-2007, Ext JS, LLC.
58173  *
58174  * Originally Released Under LGPL - original licence link has changed is not relivant.
58175  *
58176  * Fork - LGPL
58177  * <script type="text/javascript">
58178  */
58179  
58180 // private
58181 // This is a support class used internally by the Grid components
58182 Roo.grid.GridDragZone = function(grid, config){
58183     this.view = grid.getView();
58184     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58185     if(this.view.lockedBody){
58186         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58187         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58188     }
58189     this.scroll = false;
58190     this.grid = grid;
58191     this.ddel = document.createElement('div');
58192     this.ddel.className = 'x-grid-dd-wrap';
58193 };
58194
58195 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58196     ddGroup : "GridDD",
58197
58198     getDragData : function(e){
58199         var t = Roo.lib.Event.getTarget(e);
58200         var rowIndex = this.view.findRowIndex(t);
58201         var sm = this.grid.selModel;
58202             
58203         //Roo.log(rowIndex);
58204         
58205         if (sm.getSelectedCell) {
58206             // cell selection..
58207             if (!sm.getSelectedCell()) {
58208                 return false;
58209             }
58210             if (rowIndex != sm.getSelectedCell()[0]) {
58211                 return false;
58212             }
58213         
58214         }
58215         if (sm.getSelections && sm.getSelections().length < 1) {
58216             return false;
58217         }
58218         
58219         
58220         // before it used to all dragging of unseleted... - now we dont do that.
58221         if(rowIndex !== false){
58222             
58223             // if editorgrid.. 
58224             
58225             
58226             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58227                
58228             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58229               //  
58230             //}
58231             if (e.hasModifier()){
58232                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58233             }
58234             
58235             Roo.log("getDragData");
58236             
58237             return {
58238                 grid: this.grid,
58239                 ddel: this.ddel,
58240                 rowIndex: rowIndex,
58241                 selections: sm.getSelections ? sm.getSelections() : (
58242                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58243             };
58244         }
58245         return false;
58246     },
58247     
58248     
58249     onInitDrag : function(e){
58250         var data = this.dragData;
58251         this.ddel.innerHTML = this.grid.getDragDropText();
58252         this.proxy.update(this.ddel);
58253         // fire start drag?
58254     },
58255
58256     afterRepair : function(){
58257         this.dragging = false;
58258     },
58259
58260     getRepairXY : function(e, data){
58261         return false;
58262     },
58263
58264     onEndDrag : function(data, e){
58265         // fire end drag?
58266     },
58267
58268     onValidDrop : function(dd, e, id){
58269         // fire drag drop?
58270         this.hideProxy();
58271     },
58272
58273     beforeInvalidDrop : function(e, id){
58274
58275     }
58276 });/*
58277  * Based on:
58278  * Ext JS Library 1.1.1
58279  * Copyright(c) 2006-2007, Ext JS, LLC.
58280  *
58281  * Originally Released Under LGPL - original licence link has changed is not relivant.
58282  *
58283  * Fork - LGPL
58284  * <script type="text/javascript">
58285  */
58286  
58287
58288 /**
58289  * @class Roo.grid.ColumnModel
58290  * @extends Roo.util.Observable
58291  * This is the default implementation of a ColumnModel used by the Grid. It defines
58292  * the columns in the grid.
58293  * <br>Usage:<br>
58294  <pre><code>
58295  var colModel = new Roo.grid.ColumnModel([
58296         {header: "Ticker", width: 60, sortable: true, locked: true},
58297         {header: "Company Name", width: 150, sortable: true},
58298         {header: "Market Cap.", width: 100, sortable: true},
58299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58300         {header: "Employees", width: 100, sortable: true, resizable: false}
58301  ]);
58302  </code></pre>
58303  * <p>
58304  
58305  * The config options listed for this class are options which may appear in each
58306  * individual column definition.
58307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58308  * @constructor
58309  * @param {Object} config An Array of column config objects. See this class's
58310  * config objects for details.
58311 */
58312 Roo.grid.ColumnModel = function(config){
58313         /**
58314      * The config passed into the constructor
58315      */
58316     this.config = []; //config;
58317     this.lookup = {};
58318
58319     // if no id, create one
58320     // if the column does not have a dataIndex mapping,
58321     // map it to the order it is in the config
58322     for(var i = 0, len = config.length; i < len; i++){
58323         this.addColumn(config[i]);
58324         
58325     }
58326
58327     /**
58328      * The width of columns which have no width specified (defaults to 100)
58329      * @type Number
58330      */
58331     this.defaultWidth = 100;
58332
58333     /**
58334      * Default sortable of columns which have no sortable specified (defaults to false)
58335      * @type Boolean
58336      */
58337     this.defaultSortable = false;
58338
58339     this.addEvents({
58340         /**
58341              * @event widthchange
58342              * Fires when the width of a column changes.
58343              * @param {ColumnModel} this
58344              * @param {Number} columnIndex The column index
58345              * @param {Number} newWidth The new width
58346              */
58347             "widthchange": true,
58348         /**
58349              * @event headerchange
58350              * Fires when the text of a header changes.
58351              * @param {ColumnModel} this
58352              * @param {Number} columnIndex The column index
58353              * @param {Number} newText The new header text
58354              */
58355             "headerchange": true,
58356         /**
58357              * @event hiddenchange
58358              * Fires when a column is hidden or "unhidden".
58359              * @param {ColumnModel} this
58360              * @param {Number} columnIndex The column index
58361              * @param {Boolean} hidden true if hidden, false otherwise
58362              */
58363             "hiddenchange": true,
58364             /**
58365          * @event columnmoved
58366          * Fires when a column is moved.
58367          * @param {ColumnModel} this
58368          * @param {Number} oldIndex
58369          * @param {Number} newIndex
58370          */
58371         "columnmoved" : true,
58372         /**
58373          * @event columlockchange
58374          * Fires when a column's locked state is changed
58375          * @param {ColumnModel} this
58376          * @param {Number} colIndex
58377          * @param {Boolean} locked true if locked
58378          */
58379         "columnlockchange" : true
58380     });
58381     Roo.grid.ColumnModel.superclass.constructor.call(this);
58382 };
58383 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58384     /**
58385      * @cfg {String} header The header text to display in the Grid view.
58386      */
58387     /**
58388      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58389      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58390      * specified, the column's index is used as an index into the Record's data Array.
58391      */
58392     /**
58393      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58394      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58395      */
58396     /**
58397      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58398      * Defaults to the value of the {@link #defaultSortable} property.
58399      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58400      */
58401     /**
58402      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58403      */
58404     /**
58405      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58406      */
58407     /**
58408      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58409      */
58410     /**
58411      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58412      */
58413     /**
58414      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58415      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58416      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58417      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58418      */
58419        /**
58420      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58421      */
58422     /**
58423      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58424      */
58425     /**
58426      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58427      */
58428     /**
58429      * @cfg {String} cursor (Optional)
58430      */
58431     /**
58432      * @cfg {String} tooltip (Optional)
58433      */
58434     /**
58435      * @cfg {Number} xs (Optional)
58436      */
58437     /**
58438      * @cfg {Number} sm (Optional)
58439      */
58440     /**
58441      * @cfg {Number} md (Optional)
58442      */
58443     /**
58444      * @cfg {Number} lg (Optional)
58445      */
58446     /**
58447      * Returns the id of the column at the specified index.
58448      * @param {Number} index The column index
58449      * @return {String} the id
58450      */
58451     getColumnId : function(index){
58452         return this.config[index].id;
58453     },
58454
58455     /**
58456      * Returns the column for a specified id.
58457      * @param {String} id The column id
58458      * @return {Object} the column
58459      */
58460     getColumnById : function(id){
58461         return this.lookup[id];
58462     },
58463
58464     
58465     /**
58466      * Returns the column Object for a specified dataIndex.
58467      * @param {String} dataIndex The column dataIndex
58468      * @return {Object|Boolean} the column or false if not found
58469      */
58470     getColumnByDataIndex: function(dataIndex){
58471         var index = this.findColumnIndex(dataIndex);
58472         return index > -1 ? this.config[index] : false;
58473     },
58474     
58475     /**
58476      * Returns the index for a specified column id.
58477      * @param {String} id The column id
58478      * @return {Number} the index, or -1 if not found
58479      */
58480     getIndexById : function(id){
58481         for(var i = 0, len = this.config.length; i < len; i++){
58482             if(this.config[i].id == id){
58483                 return i;
58484             }
58485         }
58486         return -1;
58487     },
58488     
58489     /**
58490      * Returns the index for a specified column dataIndex.
58491      * @param {String} dataIndex The column dataIndex
58492      * @return {Number} the index, or -1 if not found
58493      */
58494     
58495     findColumnIndex : function(dataIndex){
58496         for(var i = 0, len = this.config.length; i < len; i++){
58497             if(this.config[i].dataIndex == dataIndex){
58498                 return i;
58499             }
58500         }
58501         return -1;
58502     },
58503     
58504     
58505     moveColumn : function(oldIndex, newIndex){
58506         var c = this.config[oldIndex];
58507         this.config.splice(oldIndex, 1);
58508         this.config.splice(newIndex, 0, c);
58509         this.dataMap = null;
58510         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58511     },
58512
58513     isLocked : function(colIndex){
58514         return this.config[colIndex].locked === true;
58515     },
58516
58517     setLocked : function(colIndex, value, suppressEvent){
58518         if(this.isLocked(colIndex) == value){
58519             return;
58520         }
58521         this.config[colIndex].locked = value;
58522         if(!suppressEvent){
58523             this.fireEvent("columnlockchange", this, colIndex, value);
58524         }
58525     },
58526
58527     getTotalLockedWidth : function(){
58528         var totalWidth = 0;
58529         for(var i = 0; i < this.config.length; i++){
58530             if(this.isLocked(i) && !this.isHidden(i)){
58531                 this.totalWidth += this.getColumnWidth(i);
58532             }
58533         }
58534         return totalWidth;
58535     },
58536
58537     getLockedCount : function(){
58538         for(var i = 0, len = this.config.length; i < len; i++){
58539             if(!this.isLocked(i)){
58540                 return i;
58541             }
58542         }
58543         
58544         return this.config.length;
58545     },
58546
58547     /**
58548      * Returns the number of columns.
58549      * @return {Number}
58550      */
58551     getColumnCount : function(visibleOnly){
58552         if(visibleOnly === true){
58553             var c = 0;
58554             for(var i = 0, len = this.config.length; i < len; i++){
58555                 if(!this.isHidden(i)){
58556                     c++;
58557                 }
58558             }
58559             return c;
58560         }
58561         return this.config.length;
58562     },
58563
58564     /**
58565      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58566      * @param {Function} fn
58567      * @param {Object} scope (optional)
58568      * @return {Array} result
58569      */
58570     getColumnsBy : function(fn, scope){
58571         var r = [];
58572         for(var i = 0, len = this.config.length; i < len; i++){
58573             var c = this.config[i];
58574             if(fn.call(scope||this, c, i) === true){
58575                 r[r.length] = c;
58576             }
58577         }
58578         return r;
58579     },
58580
58581     /**
58582      * Returns true if the specified column is sortable.
58583      * @param {Number} col The column index
58584      * @return {Boolean}
58585      */
58586     isSortable : function(col){
58587         if(typeof this.config[col].sortable == "undefined"){
58588             return this.defaultSortable;
58589         }
58590         return this.config[col].sortable;
58591     },
58592
58593     /**
58594      * Returns the rendering (formatting) function defined for the column.
58595      * @param {Number} col The column index.
58596      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58597      */
58598     getRenderer : function(col){
58599         if(!this.config[col].renderer){
58600             return Roo.grid.ColumnModel.defaultRenderer;
58601         }
58602         return this.config[col].renderer;
58603     },
58604
58605     /**
58606      * Sets the rendering (formatting) function for a column.
58607      * @param {Number} col The column index
58608      * @param {Function} fn The function to use to process the cell's raw data
58609      * to return HTML markup for the grid view. The render function is called with
58610      * the following parameters:<ul>
58611      * <li>Data value.</li>
58612      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58613      * <li>css A CSS style string to apply to the table cell.</li>
58614      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58615      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58616      * <li>Row index</li>
58617      * <li>Column index</li>
58618      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58619      */
58620     setRenderer : function(col, fn){
58621         this.config[col].renderer = fn;
58622     },
58623
58624     /**
58625      * Returns the width for the specified column.
58626      * @param {Number} col The column index
58627      * @return {Number}
58628      */
58629     getColumnWidth : function(col){
58630         return this.config[col].width * 1 || this.defaultWidth;
58631     },
58632
58633     /**
58634      * Sets the width for a column.
58635      * @param {Number} col The column index
58636      * @param {Number} width The new width
58637      */
58638     setColumnWidth : function(col, width, suppressEvent){
58639         this.config[col].width = width;
58640         this.totalWidth = null;
58641         if(!suppressEvent){
58642              this.fireEvent("widthchange", this, col, width);
58643         }
58644     },
58645
58646     /**
58647      * Returns the total width of all columns.
58648      * @param {Boolean} includeHidden True to include hidden column widths
58649      * @return {Number}
58650      */
58651     getTotalWidth : function(includeHidden){
58652         if(!this.totalWidth){
58653             this.totalWidth = 0;
58654             for(var i = 0, len = this.config.length; i < len; i++){
58655                 if(includeHidden || !this.isHidden(i)){
58656                     this.totalWidth += this.getColumnWidth(i);
58657                 }
58658             }
58659         }
58660         return this.totalWidth;
58661     },
58662
58663     /**
58664      * Returns the header for the specified column.
58665      * @param {Number} col The column index
58666      * @return {String}
58667      */
58668     getColumnHeader : function(col){
58669         return this.config[col].header;
58670     },
58671
58672     /**
58673      * Sets the header for a column.
58674      * @param {Number} col The column index
58675      * @param {String} header The new header
58676      */
58677     setColumnHeader : function(col, header){
58678         this.config[col].header = header;
58679         this.fireEvent("headerchange", this, col, header);
58680     },
58681
58682     /**
58683      * Returns the tooltip for the specified column.
58684      * @param {Number} col The column index
58685      * @return {String}
58686      */
58687     getColumnTooltip : function(col){
58688             return this.config[col].tooltip;
58689     },
58690     /**
58691      * Sets the tooltip for a column.
58692      * @param {Number} col The column index
58693      * @param {String} tooltip The new tooltip
58694      */
58695     setColumnTooltip : function(col, tooltip){
58696             this.config[col].tooltip = tooltip;
58697     },
58698
58699     /**
58700      * Returns the dataIndex for the specified column.
58701      * @param {Number} col The column index
58702      * @return {Number}
58703      */
58704     getDataIndex : function(col){
58705         return this.config[col].dataIndex;
58706     },
58707
58708     /**
58709      * Sets the dataIndex for a column.
58710      * @param {Number} col The column index
58711      * @param {Number} dataIndex The new dataIndex
58712      */
58713     setDataIndex : function(col, dataIndex){
58714         this.config[col].dataIndex = dataIndex;
58715     },
58716
58717     
58718     
58719     /**
58720      * Returns true if the cell is editable.
58721      * @param {Number} colIndex The column index
58722      * @param {Number} rowIndex The row index - this is nto actually used..?
58723      * @return {Boolean}
58724      */
58725     isCellEditable : function(colIndex, rowIndex){
58726         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58727     },
58728
58729     /**
58730      * Returns the editor defined for the cell/column.
58731      * return false or null to disable editing.
58732      * @param {Number} colIndex The column index
58733      * @param {Number} rowIndex The row index
58734      * @return {Object}
58735      */
58736     getCellEditor : function(colIndex, rowIndex){
58737         return this.config[colIndex].editor;
58738     },
58739
58740     /**
58741      * Sets if a column is editable.
58742      * @param {Number} col The column index
58743      * @param {Boolean} editable True if the column is editable
58744      */
58745     setEditable : function(col, editable){
58746         this.config[col].editable = editable;
58747     },
58748
58749
58750     /**
58751      * Returns true if the column is hidden.
58752      * @param {Number} colIndex The column index
58753      * @return {Boolean}
58754      */
58755     isHidden : function(colIndex){
58756         return this.config[colIndex].hidden;
58757     },
58758
58759
58760     /**
58761      * Returns true if the column width cannot be changed
58762      */
58763     isFixed : function(colIndex){
58764         return this.config[colIndex].fixed;
58765     },
58766
58767     /**
58768      * Returns true if the column can be resized
58769      * @return {Boolean}
58770      */
58771     isResizable : function(colIndex){
58772         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58773     },
58774     /**
58775      * Sets if a column is hidden.
58776      * @param {Number} colIndex The column index
58777      * @param {Boolean} hidden True if the column is hidden
58778      */
58779     setHidden : function(colIndex, hidden){
58780         this.config[colIndex].hidden = hidden;
58781         this.totalWidth = null;
58782         this.fireEvent("hiddenchange", this, colIndex, hidden);
58783     },
58784
58785     /**
58786      * Sets the editor for a column.
58787      * @param {Number} col The column index
58788      * @param {Object} editor The editor object
58789      */
58790     setEditor : function(col, editor){
58791         this.config[col].editor = editor;
58792     },
58793     /**
58794      * Add a column (experimental...) - defaults to adding to the end..
58795      * @param {Object} config 
58796     */
58797     addColumn : function(c)
58798     {
58799     
58800         var i = this.config.length;
58801         this.config[i] = c;
58802         
58803         if(typeof c.dataIndex == "undefined"){
58804             c.dataIndex = i;
58805         }
58806         if(typeof c.renderer == "string"){
58807             c.renderer = Roo.util.Format[c.renderer];
58808         }
58809         if(typeof c.id == "undefined"){
58810             c.id = Roo.id();
58811         }
58812         if(c.editor && c.editor.xtype){
58813             c.editor  = Roo.factory(c.editor, Roo.grid);
58814         }
58815         if(c.editor && c.editor.isFormField){
58816             c.editor = new Roo.grid.GridEditor(c.editor);
58817         }
58818         this.lookup[c.id] = c;
58819     }
58820     
58821 });
58822
58823 Roo.grid.ColumnModel.defaultRenderer = function(value)
58824 {
58825     if(typeof value == "object") {
58826         return value;
58827     }
58828         if(typeof value == "string" && value.length < 1){
58829             return "&#160;";
58830         }
58831     
58832         return String.format("{0}", value);
58833 };
58834
58835 // Alias for backwards compatibility
58836 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58837 /*
58838  * Based on:
58839  * Ext JS Library 1.1.1
58840  * Copyright(c) 2006-2007, Ext JS, LLC.
58841  *
58842  * Originally Released Under LGPL - original licence link has changed is not relivant.
58843  *
58844  * Fork - LGPL
58845  * <script type="text/javascript">
58846  */
58847
58848 /**
58849  * @class Roo.grid.AbstractSelectionModel
58850  * @extends Roo.util.Observable
58851  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58852  * implemented by descendant classes.  This class should not be directly instantiated.
58853  * @constructor
58854  */
58855 Roo.grid.AbstractSelectionModel = function(){
58856     this.locked = false;
58857     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58858 };
58859
58860 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58861     /** @ignore Called by the grid automatically. Do not call directly. */
58862     init : function(grid){
58863         this.grid = grid;
58864         this.initEvents();
58865     },
58866
58867     /**
58868      * Locks the selections.
58869      */
58870     lock : function(){
58871         this.locked = true;
58872     },
58873
58874     /**
58875      * Unlocks the selections.
58876      */
58877     unlock : function(){
58878         this.locked = false;
58879     },
58880
58881     /**
58882      * Returns true if the selections are locked.
58883      * @return {Boolean}
58884      */
58885     isLocked : function(){
58886         return this.locked;
58887     }
58888 });/*
58889  * Based on:
58890  * Ext JS Library 1.1.1
58891  * Copyright(c) 2006-2007, Ext JS, LLC.
58892  *
58893  * Originally Released Under LGPL - original licence link has changed is not relivant.
58894  *
58895  * Fork - LGPL
58896  * <script type="text/javascript">
58897  */
58898 /**
58899  * @extends Roo.grid.AbstractSelectionModel
58900  * @class Roo.grid.RowSelectionModel
58901  * The default SelectionModel used by {@link Roo.grid.Grid}.
58902  * It supports multiple selections and keyboard selection/navigation. 
58903  * @constructor
58904  * @param {Object} config
58905  */
58906 Roo.grid.RowSelectionModel = function(config){
58907     Roo.apply(this, config);
58908     this.selections = new Roo.util.MixedCollection(false, function(o){
58909         return o.id;
58910     });
58911
58912     this.last = false;
58913     this.lastActive = false;
58914
58915     this.addEvents({
58916         /**
58917         * @event selectionchange
58918         * Fires when the selection changes
58919         * @param {SelectionModel} this
58920         */
58921        "selectionchange" : true,
58922        /**
58923         * @event afterselectionchange
58924         * Fires after the selection changes (eg. by key press or clicking)
58925         * @param {SelectionModel} this
58926         */
58927        "afterselectionchange" : true,
58928        /**
58929         * @event beforerowselect
58930         * Fires when a row is selected being selected, return false to cancel.
58931         * @param {SelectionModel} this
58932         * @param {Number} rowIndex The selected index
58933         * @param {Boolean} keepExisting False if other selections will be cleared
58934         */
58935        "beforerowselect" : true,
58936        /**
58937         * @event rowselect
58938         * Fires when a row is selected.
58939         * @param {SelectionModel} this
58940         * @param {Number} rowIndex The selected index
58941         * @param {Roo.data.Record} r The record
58942         */
58943        "rowselect" : true,
58944        /**
58945         * @event rowdeselect
58946         * Fires when a row is deselected.
58947         * @param {SelectionModel} this
58948         * @param {Number} rowIndex The selected index
58949         */
58950         "rowdeselect" : true
58951     });
58952     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58953     this.locked = false;
58954 };
58955
58956 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58957     /**
58958      * @cfg {Boolean} singleSelect
58959      * True to allow selection of only one row at a time (defaults to false)
58960      */
58961     singleSelect : false,
58962
58963     // private
58964     initEvents : function(){
58965
58966         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58967             this.grid.on("mousedown", this.handleMouseDown, this);
58968         }else{ // allow click to work like normal
58969             this.grid.on("rowclick", this.handleDragableRowClick, this);
58970         }
58971         // bootstrap does not have a view..
58972         var view = this.grid.view ? this.grid.view : this.grid;
58973         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58974             "up" : function(e){
58975                 if(!e.shiftKey){
58976                     this.selectPrevious(e.shiftKey);
58977                 }else if(this.last !== false && this.lastActive !== false){
58978                     var last = this.last;
58979                     this.selectRange(this.last,  this.lastActive-1);
58980                     view.focusRow(this.lastActive);
58981                     if(last !== false){
58982                         this.last = last;
58983                     }
58984                 }else{
58985                     this.selectFirstRow();
58986                 }
58987                 this.fireEvent("afterselectionchange", this);
58988             },
58989             "down" : function(e){
58990                 if(!e.shiftKey){
58991                     this.selectNext(e.shiftKey);
58992                 }else if(this.last !== false && this.lastActive !== false){
58993                     var last = this.last;
58994                     this.selectRange(this.last,  this.lastActive+1);
58995                     view.focusRow(this.lastActive);
58996                     if(last !== false){
58997                         this.last = last;
58998                     }
58999                 }else{
59000                     this.selectFirstRow();
59001                 }
59002                 this.fireEvent("afterselectionchange", this);
59003             },
59004             scope: this
59005         });
59006
59007          
59008         view.on("refresh", this.onRefresh, this);
59009         view.on("rowupdated", this.onRowUpdated, this);
59010         view.on("rowremoved", this.onRemove, this);
59011     },
59012
59013     // private
59014     onRefresh : function(){
59015         var ds = this.grid.ds, i, v = this.grid.view;
59016         var s = this.selections;
59017         s.each(function(r){
59018             if((i = ds.indexOfId(r.id)) != -1){
59019                 v.onRowSelect(i);
59020                 s.add(ds.getAt(i)); // updating the selection relate data
59021             }else{
59022                 s.remove(r);
59023             }
59024         });
59025     },
59026
59027     // private
59028     onRemove : function(v, index, r){
59029         this.selections.remove(r);
59030     },
59031
59032     // private
59033     onRowUpdated : function(v, index, r){
59034         if(this.isSelected(r)){
59035             v.onRowSelect(index);
59036         }
59037     },
59038
59039     /**
59040      * Select records.
59041      * @param {Array} records The records to select
59042      * @param {Boolean} keepExisting (optional) True to keep existing selections
59043      */
59044     selectRecords : function(records, keepExisting){
59045         if(!keepExisting){
59046             this.clearSelections();
59047         }
59048         var ds = this.grid.ds;
59049         for(var i = 0, len = records.length; i < len; i++){
59050             this.selectRow(ds.indexOf(records[i]), true);
59051         }
59052     },
59053
59054     /**
59055      * Gets the number of selected rows.
59056      * @return {Number}
59057      */
59058     getCount : function(){
59059         return this.selections.length;
59060     },
59061
59062     /**
59063      * Selects the first row in the grid.
59064      */
59065     selectFirstRow : function(){
59066         this.selectRow(0);
59067     },
59068
59069     /**
59070      * Select the last row.
59071      * @param {Boolean} keepExisting (optional) True to keep existing selections
59072      */
59073     selectLastRow : function(keepExisting){
59074         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59075     },
59076
59077     /**
59078      * Selects the row immediately following the last selected row.
59079      * @param {Boolean} keepExisting (optional) True to keep existing selections
59080      */
59081     selectNext : function(keepExisting){
59082         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59083             this.selectRow(this.last+1, keepExisting);
59084             var view = this.grid.view ? this.grid.view : this.grid;
59085             view.focusRow(this.last);
59086         }
59087     },
59088
59089     /**
59090      * Selects the row that precedes the last selected row.
59091      * @param {Boolean} keepExisting (optional) True to keep existing selections
59092      */
59093     selectPrevious : function(keepExisting){
59094         if(this.last){
59095             this.selectRow(this.last-1, keepExisting);
59096             var view = this.grid.view ? this.grid.view : this.grid;
59097             view.focusRow(this.last);
59098         }
59099     },
59100
59101     /**
59102      * Returns the selected records
59103      * @return {Array} Array of selected records
59104      */
59105     getSelections : function(){
59106         return [].concat(this.selections.items);
59107     },
59108
59109     /**
59110      * Returns the first selected record.
59111      * @return {Record}
59112      */
59113     getSelected : function(){
59114         return this.selections.itemAt(0);
59115     },
59116
59117
59118     /**
59119      * Clears all selections.
59120      */
59121     clearSelections : function(fast){
59122         if(this.locked) {
59123             return;
59124         }
59125         if(fast !== true){
59126             var ds = this.grid.ds;
59127             var s = this.selections;
59128             s.each(function(r){
59129                 this.deselectRow(ds.indexOfId(r.id));
59130             }, this);
59131             s.clear();
59132         }else{
59133             this.selections.clear();
59134         }
59135         this.last = false;
59136     },
59137
59138
59139     /**
59140      * Selects all rows.
59141      */
59142     selectAll : function(){
59143         if(this.locked) {
59144             return;
59145         }
59146         this.selections.clear();
59147         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59148             this.selectRow(i, true);
59149         }
59150     },
59151
59152     /**
59153      * Returns True if there is a selection.
59154      * @return {Boolean}
59155      */
59156     hasSelection : function(){
59157         return this.selections.length > 0;
59158     },
59159
59160     /**
59161      * Returns True if the specified row is selected.
59162      * @param {Number/Record} record The record or index of the record to check
59163      * @return {Boolean}
59164      */
59165     isSelected : function(index){
59166         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59167         return (r && this.selections.key(r.id) ? true : false);
59168     },
59169
59170     /**
59171      * Returns True if the specified record id is selected.
59172      * @param {String} id The id of record to check
59173      * @return {Boolean}
59174      */
59175     isIdSelected : function(id){
59176         return (this.selections.key(id) ? true : false);
59177     },
59178
59179     // private
59180     handleMouseDown : function(e, t)
59181     {
59182         var view = this.grid.view ? this.grid.view : this.grid;
59183         var rowIndex;
59184         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59185             return;
59186         };
59187         if(e.shiftKey && this.last !== false){
59188             var last = this.last;
59189             this.selectRange(last, rowIndex, e.ctrlKey);
59190             this.last = last; // reset the last
59191             view.focusRow(rowIndex);
59192         }else{
59193             var isSelected = this.isSelected(rowIndex);
59194             if(e.button !== 0 && isSelected){
59195                 view.focusRow(rowIndex);
59196             }else if(e.ctrlKey && isSelected){
59197                 this.deselectRow(rowIndex);
59198             }else if(!isSelected){
59199                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59200                 view.focusRow(rowIndex);
59201             }
59202         }
59203         this.fireEvent("afterselectionchange", this);
59204     },
59205     // private
59206     handleDragableRowClick :  function(grid, rowIndex, e) 
59207     {
59208         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59209             this.selectRow(rowIndex, false);
59210             var view = this.grid.view ? this.grid.view : this.grid;
59211             view.focusRow(rowIndex);
59212              this.fireEvent("afterselectionchange", this);
59213         }
59214     },
59215     
59216     /**
59217      * Selects multiple rows.
59218      * @param {Array} rows Array of the indexes of the row to select
59219      * @param {Boolean} keepExisting (optional) True to keep existing selections
59220      */
59221     selectRows : function(rows, keepExisting){
59222         if(!keepExisting){
59223             this.clearSelections();
59224         }
59225         for(var i = 0, len = rows.length; i < len; i++){
59226             this.selectRow(rows[i], true);
59227         }
59228     },
59229
59230     /**
59231      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59232      * @param {Number} startRow The index of the first row in the range
59233      * @param {Number} endRow The index of the last row in the range
59234      * @param {Boolean} keepExisting (optional) True to retain existing selections
59235      */
59236     selectRange : function(startRow, endRow, keepExisting){
59237         if(this.locked) {
59238             return;
59239         }
59240         if(!keepExisting){
59241             this.clearSelections();
59242         }
59243         if(startRow <= endRow){
59244             for(var i = startRow; i <= endRow; i++){
59245                 this.selectRow(i, true);
59246             }
59247         }else{
59248             for(var i = startRow; i >= endRow; i--){
59249                 this.selectRow(i, true);
59250             }
59251         }
59252     },
59253
59254     /**
59255      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59256      * @param {Number} startRow The index of the first row in the range
59257      * @param {Number} endRow The index of the last row in the range
59258      */
59259     deselectRange : function(startRow, endRow, preventViewNotify){
59260         if(this.locked) {
59261             return;
59262         }
59263         for(var i = startRow; i <= endRow; i++){
59264             this.deselectRow(i, preventViewNotify);
59265         }
59266     },
59267
59268     /**
59269      * Selects a row.
59270      * @param {Number} row The index of the row to select
59271      * @param {Boolean} keepExisting (optional) True to keep existing selections
59272      */
59273     selectRow : function(index, keepExisting, preventViewNotify){
59274         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59275             return;
59276         }
59277         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59278             if(!keepExisting || this.singleSelect){
59279                 this.clearSelections();
59280             }
59281             var r = this.grid.ds.getAt(index);
59282             this.selections.add(r);
59283             this.last = this.lastActive = index;
59284             if(!preventViewNotify){
59285                 var view = this.grid.view ? this.grid.view : this.grid;
59286                 view.onRowSelect(index);
59287             }
59288             this.fireEvent("rowselect", this, index, r);
59289             this.fireEvent("selectionchange", this);
59290         }
59291     },
59292
59293     /**
59294      * Deselects a row.
59295      * @param {Number} row The index of the row to deselect
59296      */
59297     deselectRow : function(index, preventViewNotify){
59298         if(this.locked) {
59299             return;
59300         }
59301         if(this.last == index){
59302             this.last = false;
59303         }
59304         if(this.lastActive == index){
59305             this.lastActive = false;
59306         }
59307         var r = this.grid.ds.getAt(index);
59308         this.selections.remove(r);
59309         if(!preventViewNotify){
59310             var view = this.grid.view ? this.grid.view : this.grid;
59311             view.onRowDeselect(index);
59312         }
59313         this.fireEvent("rowdeselect", this, index);
59314         this.fireEvent("selectionchange", this);
59315     },
59316
59317     // private
59318     restoreLast : function(){
59319         if(this._last){
59320             this.last = this._last;
59321         }
59322     },
59323
59324     // private
59325     acceptsNav : function(row, col, cm){
59326         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59327     },
59328
59329     // private
59330     onEditorKey : function(field, e){
59331         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59332         if(k == e.TAB){
59333             e.stopEvent();
59334             ed.completeEdit();
59335             if(e.shiftKey){
59336                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59337             }else{
59338                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59339             }
59340         }else if(k == e.ENTER && !e.ctrlKey){
59341             e.stopEvent();
59342             ed.completeEdit();
59343             if(e.shiftKey){
59344                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59345             }else{
59346                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59347             }
59348         }else if(k == e.ESC){
59349             ed.cancelEdit();
59350         }
59351         if(newCell){
59352             g.startEditing(newCell[0], newCell[1]);
59353         }
59354     }
59355 });/*
59356  * Based on:
59357  * Ext JS Library 1.1.1
59358  * Copyright(c) 2006-2007, Ext JS, LLC.
59359  *
59360  * Originally Released Under LGPL - original licence link has changed is not relivant.
59361  *
59362  * Fork - LGPL
59363  * <script type="text/javascript">
59364  */
59365 /**
59366  * @class Roo.grid.CellSelectionModel
59367  * @extends Roo.grid.AbstractSelectionModel
59368  * This class provides the basic implementation for cell selection in a grid.
59369  * @constructor
59370  * @param {Object} config The object containing the configuration of this model.
59371  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59372  */
59373 Roo.grid.CellSelectionModel = function(config){
59374     Roo.apply(this, config);
59375
59376     this.selection = null;
59377
59378     this.addEvents({
59379         /**
59380              * @event beforerowselect
59381              * Fires before a cell is selected.
59382              * @param {SelectionModel} this
59383              * @param {Number} rowIndex The selected row index
59384              * @param {Number} colIndex The selected cell index
59385              */
59386             "beforecellselect" : true,
59387         /**
59388              * @event cellselect
59389              * Fires when a cell is selected.
59390              * @param {SelectionModel} this
59391              * @param {Number} rowIndex The selected row index
59392              * @param {Number} colIndex The selected cell index
59393              */
59394             "cellselect" : true,
59395         /**
59396              * @event selectionchange
59397              * Fires when the active selection changes.
59398              * @param {SelectionModel} this
59399              * @param {Object} selection null for no selection or an object (o) with two properties
59400                 <ul>
59401                 <li>o.record: the record object for the row the selection is in</li>
59402                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59403                 </ul>
59404              */
59405             "selectionchange" : true,
59406         /**
59407              * @event tabend
59408              * Fires when the tab (or enter) was pressed on the last editable cell
59409              * You can use this to trigger add new row.
59410              * @param {SelectionModel} this
59411              */
59412             "tabend" : true,
59413          /**
59414              * @event beforeeditnext
59415              * Fires before the next editable sell is made active
59416              * You can use this to skip to another cell or fire the tabend
59417              *    if you set cell to false
59418              * @param {Object} eventdata object : { cell : [ row, col ] } 
59419              */
59420             "beforeeditnext" : true
59421     });
59422     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59423 };
59424
59425 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59426     
59427     enter_is_tab: false,
59428
59429     /** @ignore */
59430     initEvents : function(){
59431         this.grid.on("mousedown", this.handleMouseDown, this);
59432         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59433         var view = this.grid.view;
59434         view.on("refresh", this.onViewChange, this);
59435         view.on("rowupdated", this.onRowUpdated, this);
59436         view.on("beforerowremoved", this.clearSelections, this);
59437         view.on("beforerowsinserted", this.clearSelections, this);
59438         if(this.grid.isEditor){
59439             this.grid.on("beforeedit", this.beforeEdit,  this);
59440         }
59441     },
59442
59443         //private
59444     beforeEdit : function(e){
59445         this.select(e.row, e.column, false, true, e.record);
59446     },
59447
59448         //private
59449     onRowUpdated : function(v, index, r){
59450         if(this.selection && this.selection.record == r){
59451             v.onCellSelect(index, this.selection.cell[1]);
59452         }
59453     },
59454
59455         //private
59456     onViewChange : function(){
59457         this.clearSelections(true);
59458     },
59459
59460         /**
59461          * Returns the currently selected cell,.
59462          * @return {Array} The selected cell (row, column) or null if none selected.
59463          */
59464     getSelectedCell : function(){
59465         return this.selection ? this.selection.cell : null;
59466     },
59467
59468     /**
59469      * Clears all selections.
59470      * @param {Boolean} true to prevent the gridview from being notified about the change.
59471      */
59472     clearSelections : function(preventNotify){
59473         var s = this.selection;
59474         if(s){
59475             if(preventNotify !== true){
59476                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59477             }
59478             this.selection = null;
59479             this.fireEvent("selectionchange", this, null);
59480         }
59481     },
59482
59483     /**
59484      * Returns true if there is a selection.
59485      * @return {Boolean}
59486      */
59487     hasSelection : function(){
59488         return this.selection ? true : false;
59489     },
59490
59491     /** @ignore */
59492     handleMouseDown : function(e, t){
59493         var v = this.grid.getView();
59494         if(this.isLocked()){
59495             return;
59496         };
59497         var row = v.findRowIndex(t);
59498         var cell = v.findCellIndex(t);
59499         if(row !== false && cell !== false){
59500             this.select(row, cell);
59501         }
59502     },
59503
59504     /**
59505      * Selects a cell.
59506      * @param {Number} rowIndex
59507      * @param {Number} collIndex
59508      */
59509     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59510         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59511             this.clearSelections();
59512             r = r || this.grid.dataSource.getAt(rowIndex);
59513             this.selection = {
59514                 record : r,
59515                 cell : [rowIndex, colIndex]
59516             };
59517             if(!preventViewNotify){
59518                 var v = this.grid.getView();
59519                 v.onCellSelect(rowIndex, colIndex);
59520                 if(preventFocus !== true){
59521                     v.focusCell(rowIndex, colIndex);
59522                 }
59523             }
59524             this.fireEvent("cellselect", this, rowIndex, colIndex);
59525             this.fireEvent("selectionchange", this, this.selection);
59526         }
59527     },
59528
59529         //private
59530     isSelectable : function(rowIndex, colIndex, cm){
59531         return !cm.isHidden(colIndex);
59532     },
59533
59534     /** @ignore */
59535     handleKeyDown : function(e){
59536         //Roo.log('Cell Sel Model handleKeyDown');
59537         if(!e.isNavKeyPress()){
59538             return;
59539         }
59540         var g = this.grid, s = this.selection;
59541         if(!s){
59542             e.stopEvent();
59543             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59544             if(cell){
59545                 this.select(cell[0], cell[1]);
59546             }
59547             return;
59548         }
59549         var sm = this;
59550         var walk = function(row, col, step){
59551             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59552         };
59553         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59554         var newCell;
59555
59556       
59557
59558         switch(k){
59559             case e.TAB:
59560                 // handled by onEditorKey
59561                 if (g.isEditor && g.editing) {
59562                     return;
59563                 }
59564                 if(e.shiftKey) {
59565                     newCell = walk(r, c-1, -1);
59566                 } else {
59567                     newCell = walk(r, c+1, 1);
59568                 }
59569                 break;
59570             
59571             case e.DOWN:
59572                newCell = walk(r+1, c, 1);
59573                 break;
59574             
59575             case e.UP:
59576                 newCell = walk(r-1, c, -1);
59577                 break;
59578             
59579             case e.RIGHT:
59580                 newCell = walk(r, c+1, 1);
59581                 break;
59582             
59583             case e.LEFT:
59584                 newCell = walk(r, c-1, -1);
59585                 break;
59586             
59587             case e.ENTER:
59588                 
59589                 if(g.isEditor && !g.editing){
59590                    g.startEditing(r, c);
59591                    e.stopEvent();
59592                    return;
59593                 }
59594                 
59595                 
59596              break;
59597         };
59598         if(newCell){
59599             this.select(newCell[0], newCell[1]);
59600             e.stopEvent();
59601             
59602         }
59603     },
59604
59605     acceptsNav : function(row, col, cm){
59606         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59607     },
59608     /**
59609      * Selects a cell.
59610      * @param {Number} field (not used) - as it's normally used as a listener
59611      * @param {Number} e - event - fake it by using
59612      *
59613      * var e = Roo.EventObjectImpl.prototype;
59614      * e.keyCode = e.TAB
59615      *
59616      * 
59617      */
59618     onEditorKey : function(field, e){
59619         
59620         var k = e.getKey(),
59621             newCell,
59622             g = this.grid,
59623             ed = g.activeEditor,
59624             forward = false;
59625         ///Roo.log('onEditorKey' + k);
59626         
59627         
59628         if (this.enter_is_tab && k == e.ENTER) {
59629             k = e.TAB;
59630         }
59631         
59632         if(k == e.TAB){
59633             if(e.shiftKey){
59634                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59635             }else{
59636                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59637                 forward = true;
59638             }
59639             
59640             e.stopEvent();
59641             
59642         } else if(k == e.ENTER &&  !e.ctrlKey){
59643             ed.completeEdit();
59644             e.stopEvent();
59645             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59646         
59647                 } else if(k == e.ESC){
59648             ed.cancelEdit();
59649         }
59650                 
59651         if (newCell) {
59652             var ecall = { cell : newCell, forward : forward };
59653             this.fireEvent('beforeeditnext', ecall );
59654             newCell = ecall.cell;
59655                         forward = ecall.forward;
59656         }
59657                 
59658         if(newCell){
59659             //Roo.log('next cell after edit');
59660             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59661         } else if (forward) {
59662             // tabbed past last
59663             this.fireEvent.defer(100, this, ['tabend',this]);
59664         }
59665     }
59666 });/*
59667  * Based on:
59668  * Ext JS Library 1.1.1
59669  * Copyright(c) 2006-2007, Ext JS, LLC.
59670  *
59671  * Originally Released Under LGPL - original licence link has changed is not relivant.
59672  *
59673  * Fork - LGPL
59674  * <script type="text/javascript">
59675  */
59676  
59677 /**
59678  * @class Roo.grid.EditorGrid
59679  * @extends Roo.grid.Grid
59680  * Class for creating and editable grid.
59681  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59682  * The container MUST have some type of size defined for the grid to fill. The container will be 
59683  * automatically set to position relative if it isn't already.
59684  * @param {Object} dataSource The data model to bind to
59685  * @param {Object} colModel The column model with info about this grid's columns
59686  */
59687 Roo.grid.EditorGrid = function(container, config){
59688     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59689     this.getGridEl().addClass("xedit-grid");
59690
59691     if(!this.selModel){
59692         this.selModel = new Roo.grid.CellSelectionModel();
59693     }
59694
59695     this.activeEditor = null;
59696
59697         this.addEvents({
59698             /**
59699              * @event beforeedit
59700              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59701              * <ul style="padding:5px;padding-left:16px;">
59702              * <li>grid - This grid</li>
59703              * <li>record - The record being edited</li>
59704              * <li>field - The field name being edited</li>
59705              * <li>value - The value for the field being edited.</li>
59706              * <li>row - The grid row index</li>
59707              * <li>column - The grid column index</li>
59708              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59709              * </ul>
59710              * @param {Object} e An edit event (see above for description)
59711              */
59712             "beforeedit" : true,
59713             /**
59714              * @event afteredit
59715              * Fires after a cell is edited. <br />
59716              * <ul style="padding:5px;padding-left:16px;">
59717              * <li>grid - This grid</li>
59718              * <li>record - The record being edited</li>
59719              * <li>field - The field name being edited</li>
59720              * <li>value - The value being set</li>
59721              * <li>originalValue - The original value for the field, before the edit.</li>
59722              * <li>row - The grid row index</li>
59723              * <li>column - The grid column index</li>
59724              * </ul>
59725              * @param {Object} e An edit event (see above for description)
59726              */
59727             "afteredit" : true,
59728             /**
59729              * @event validateedit
59730              * Fires after a cell is edited, but before the value is set in the record. 
59731          * You can use this to modify the value being set in the field, Return false
59732              * to cancel the change. The edit event object has the following properties <br />
59733              * <ul style="padding:5px;padding-left:16px;">
59734          * <li>editor - This editor</li>
59735              * <li>grid - This grid</li>
59736              * <li>record - The record being edited</li>
59737              * <li>field - The field name being edited</li>
59738              * <li>value - The value being set</li>
59739              * <li>originalValue - The original value for the field, before the edit.</li>
59740              * <li>row - The grid row index</li>
59741              * <li>column - The grid column index</li>
59742              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59743              * </ul>
59744              * @param {Object} e An edit event (see above for description)
59745              */
59746             "validateedit" : true
59747         });
59748     this.on("bodyscroll", this.stopEditing,  this);
59749     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59750 };
59751
59752 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59753     /**
59754      * @cfg {Number} clicksToEdit
59755      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59756      */
59757     clicksToEdit: 2,
59758
59759     // private
59760     isEditor : true,
59761     // private
59762     trackMouseOver: false, // causes very odd FF errors
59763
59764     onCellDblClick : function(g, row, col){
59765         this.startEditing(row, col);
59766     },
59767
59768     onEditComplete : function(ed, value, startValue){
59769         this.editing = false;
59770         this.activeEditor = null;
59771         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59772         var r = ed.record;
59773         var field = this.colModel.getDataIndex(ed.col);
59774         var e = {
59775             grid: this,
59776             record: r,
59777             field: field,
59778             originalValue: startValue,
59779             value: value,
59780             row: ed.row,
59781             column: ed.col,
59782             cancel:false,
59783             editor: ed
59784         };
59785         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59786         cell.show();
59787           
59788         if(String(value) !== String(startValue)){
59789             
59790             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59791                 r.set(field, e.value);
59792                 // if we are dealing with a combo box..
59793                 // then we also set the 'name' colum to be the displayField
59794                 if (ed.field.displayField && ed.field.name) {
59795                     r.set(ed.field.name, ed.field.el.dom.value);
59796                 }
59797                 
59798                 delete e.cancel; //?? why!!!
59799                 this.fireEvent("afteredit", e);
59800             }
59801         } else {
59802             this.fireEvent("afteredit", e); // always fire it!
59803         }
59804         this.view.focusCell(ed.row, ed.col);
59805     },
59806
59807     /**
59808      * Starts editing the specified for the specified row/column
59809      * @param {Number} rowIndex
59810      * @param {Number} colIndex
59811      */
59812     startEditing : function(row, col){
59813         this.stopEditing();
59814         if(this.colModel.isCellEditable(col, row)){
59815             this.view.ensureVisible(row, col, true);
59816           
59817             var r = this.dataSource.getAt(row);
59818             var field = this.colModel.getDataIndex(col);
59819             var cell = Roo.get(this.view.getCell(row,col));
59820             var e = {
59821                 grid: this,
59822                 record: r,
59823                 field: field,
59824                 value: r.data[field],
59825                 row: row,
59826                 column: col,
59827                 cancel:false 
59828             };
59829             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59830                 this.editing = true;
59831                 var ed = this.colModel.getCellEditor(col, row);
59832                 
59833                 if (!ed) {
59834                     return;
59835                 }
59836                 if(!ed.rendered){
59837                     ed.render(ed.parentEl || document.body);
59838                 }
59839                 ed.field.reset();
59840                
59841                 cell.hide();
59842                 
59843                 (function(){ // complex but required for focus issues in safari, ie and opera
59844                     ed.row = row;
59845                     ed.col = col;
59846                     ed.record = r;
59847                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59848                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59849                     this.activeEditor = ed;
59850                     var v = r.data[field];
59851                     ed.startEdit(this.view.getCell(row, col), v);
59852                     // combo's with 'displayField and name set
59853                     if (ed.field.displayField && ed.field.name) {
59854                         ed.field.el.dom.value = r.data[ed.field.name];
59855                     }
59856                     
59857                     
59858                 }).defer(50, this);
59859             }
59860         }
59861     },
59862         
59863     /**
59864      * Stops any active editing
59865      */
59866     stopEditing : function(){
59867         if(this.activeEditor){
59868             this.activeEditor.completeEdit();
59869         }
59870         this.activeEditor = null;
59871     },
59872         
59873          /**
59874      * Called to get grid's drag proxy text, by default returns this.ddText.
59875      * @return {String}
59876      */
59877     getDragDropText : function(){
59878         var count = this.selModel.getSelectedCell() ? 1 : 0;
59879         return String.format(this.ddText, count, count == 1 ? '' : 's');
59880     }
59881         
59882 });/*
59883  * Based on:
59884  * Ext JS Library 1.1.1
59885  * Copyright(c) 2006-2007, Ext JS, LLC.
59886  *
59887  * Originally Released Under LGPL - original licence link has changed is not relivant.
59888  *
59889  * Fork - LGPL
59890  * <script type="text/javascript">
59891  */
59892
59893 // private - not really -- you end up using it !
59894 // This is a support class used internally by the Grid components
59895
59896 /**
59897  * @class Roo.grid.GridEditor
59898  * @extends Roo.Editor
59899  * Class for creating and editable grid elements.
59900  * @param {Object} config any settings (must include field)
59901  */
59902 Roo.grid.GridEditor = function(field, config){
59903     if (!config && field.field) {
59904         config = field;
59905         field = Roo.factory(config.field, Roo.form);
59906     }
59907     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59908     field.monitorTab = false;
59909 };
59910
59911 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59912     
59913     /**
59914      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59915      */
59916     
59917     alignment: "tl-tl",
59918     autoSize: "width",
59919     hideEl : false,
59920     cls: "x-small-editor x-grid-editor",
59921     shim:false,
59922     shadow:"frame"
59923 });/*
59924  * Based on:
59925  * Ext JS Library 1.1.1
59926  * Copyright(c) 2006-2007, Ext JS, LLC.
59927  *
59928  * Originally Released Under LGPL - original licence link has changed is not relivant.
59929  *
59930  * Fork - LGPL
59931  * <script type="text/javascript">
59932  */
59933   
59934
59935   
59936 Roo.grid.PropertyRecord = Roo.data.Record.create([
59937     {name:'name',type:'string'},  'value'
59938 ]);
59939
59940
59941 Roo.grid.PropertyStore = function(grid, source){
59942     this.grid = grid;
59943     this.store = new Roo.data.Store({
59944         recordType : Roo.grid.PropertyRecord
59945     });
59946     this.store.on('update', this.onUpdate,  this);
59947     if(source){
59948         this.setSource(source);
59949     }
59950     Roo.grid.PropertyStore.superclass.constructor.call(this);
59951 };
59952
59953
59954
59955 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59956     setSource : function(o){
59957         this.source = o;
59958         this.store.removeAll();
59959         var data = [];
59960         for(var k in o){
59961             if(this.isEditableValue(o[k])){
59962                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59963             }
59964         }
59965         this.store.loadRecords({records: data}, {}, true);
59966     },
59967
59968     onUpdate : function(ds, record, type){
59969         if(type == Roo.data.Record.EDIT){
59970             var v = record.data['value'];
59971             var oldValue = record.modified['value'];
59972             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59973                 this.source[record.id] = v;
59974                 record.commit();
59975                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59976             }else{
59977                 record.reject();
59978             }
59979         }
59980     },
59981
59982     getProperty : function(row){
59983        return this.store.getAt(row);
59984     },
59985
59986     isEditableValue: function(val){
59987         if(val && val instanceof Date){
59988             return true;
59989         }else if(typeof val == 'object' || typeof val == 'function'){
59990             return false;
59991         }
59992         return true;
59993     },
59994
59995     setValue : function(prop, value){
59996         this.source[prop] = value;
59997         this.store.getById(prop).set('value', value);
59998     },
59999
60000     getSource : function(){
60001         return this.source;
60002     }
60003 });
60004
60005 Roo.grid.PropertyColumnModel = function(grid, store){
60006     this.grid = grid;
60007     var g = Roo.grid;
60008     g.PropertyColumnModel.superclass.constructor.call(this, [
60009         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60010         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60011     ]);
60012     this.store = store;
60013     this.bselect = Roo.DomHelper.append(document.body, {
60014         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60015             {tag: 'option', value: 'true', html: 'true'},
60016             {tag: 'option', value: 'false', html: 'false'}
60017         ]
60018     });
60019     Roo.id(this.bselect);
60020     var f = Roo.form;
60021     this.editors = {
60022         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60023         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60024         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60025         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60026         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60027     };
60028     this.renderCellDelegate = this.renderCell.createDelegate(this);
60029     this.renderPropDelegate = this.renderProp.createDelegate(this);
60030 };
60031
60032 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60033     
60034     
60035     nameText : 'Name',
60036     valueText : 'Value',
60037     
60038     dateFormat : 'm/j/Y',
60039     
60040     
60041     renderDate : function(dateVal){
60042         return dateVal.dateFormat(this.dateFormat);
60043     },
60044
60045     renderBool : function(bVal){
60046         return bVal ? 'true' : 'false';
60047     },
60048
60049     isCellEditable : function(colIndex, rowIndex){
60050         return colIndex == 1;
60051     },
60052
60053     getRenderer : function(col){
60054         return col == 1 ?
60055             this.renderCellDelegate : this.renderPropDelegate;
60056     },
60057
60058     renderProp : function(v){
60059         return this.getPropertyName(v);
60060     },
60061
60062     renderCell : function(val){
60063         var rv = val;
60064         if(val instanceof Date){
60065             rv = this.renderDate(val);
60066         }else if(typeof val == 'boolean'){
60067             rv = this.renderBool(val);
60068         }
60069         return Roo.util.Format.htmlEncode(rv);
60070     },
60071
60072     getPropertyName : function(name){
60073         var pn = this.grid.propertyNames;
60074         return pn && pn[name] ? pn[name] : name;
60075     },
60076
60077     getCellEditor : function(colIndex, rowIndex){
60078         var p = this.store.getProperty(rowIndex);
60079         var n = p.data['name'], val = p.data['value'];
60080         
60081         if(typeof(this.grid.customEditors[n]) == 'string'){
60082             return this.editors[this.grid.customEditors[n]];
60083         }
60084         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60085             return this.grid.customEditors[n];
60086         }
60087         if(val instanceof Date){
60088             return this.editors['date'];
60089         }else if(typeof val == 'number'){
60090             return this.editors['number'];
60091         }else if(typeof val == 'boolean'){
60092             return this.editors['boolean'];
60093         }else{
60094             return this.editors['string'];
60095         }
60096     }
60097 });
60098
60099 /**
60100  * @class Roo.grid.PropertyGrid
60101  * @extends Roo.grid.EditorGrid
60102  * This class represents the  interface of a component based property grid control.
60103  * <br><br>Usage:<pre><code>
60104  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60105       
60106  });
60107  // set any options
60108  grid.render();
60109  * </code></pre>
60110   
60111  * @constructor
60112  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60113  * The container MUST have some type of size defined for the grid to fill. The container will be
60114  * automatically set to position relative if it isn't already.
60115  * @param {Object} config A config object that sets properties on this grid.
60116  */
60117 Roo.grid.PropertyGrid = function(container, config){
60118     config = config || {};
60119     var store = new Roo.grid.PropertyStore(this);
60120     this.store = store;
60121     var cm = new Roo.grid.PropertyColumnModel(this, store);
60122     store.store.sort('name', 'ASC');
60123     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60124         ds: store.store,
60125         cm: cm,
60126         enableColLock:false,
60127         enableColumnMove:false,
60128         stripeRows:false,
60129         trackMouseOver: false,
60130         clicksToEdit:1
60131     }, config));
60132     this.getGridEl().addClass('x-props-grid');
60133     this.lastEditRow = null;
60134     this.on('columnresize', this.onColumnResize, this);
60135     this.addEvents({
60136          /**
60137              * @event beforepropertychange
60138              * Fires before a property changes (return false to stop?)
60139              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60140              * @param {String} id Record Id
60141              * @param {String} newval New Value
60142          * @param {String} oldval Old Value
60143              */
60144         "beforepropertychange": true,
60145         /**
60146              * @event propertychange
60147              * Fires after a property changes
60148              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60149              * @param {String} id Record Id
60150              * @param {String} newval New Value
60151          * @param {String} oldval Old Value
60152              */
60153         "propertychange": true
60154     });
60155     this.customEditors = this.customEditors || {};
60156 };
60157 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60158     
60159      /**
60160      * @cfg {Object} customEditors map of colnames=> custom editors.
60161      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60162      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60163      * false disables editing of the field.
60164          */
60165     
60166       /**
60167      * @cfg {Object} propertyNames map of property Names to their displayed value
60168          */
60169     
60170     render : function(){
60171         Roo.grid.PropertyGrid.superclass.render.call(this);
60172         this.autoSize.defer(100, this);
60173     },
60174
60175     autoSize : function(){
60176         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60177         if(this.view){
60178             this.view.fitColumns();
60179         }
60180     },
60181
60182     onColumnResize : function(){
60183         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60184         this.autoSize();
60185     },
60186     /**
60187      * Sets the data for the Grid
60188      * accepts a Key => Value object of all the elements avaiable.
60189      * @param {Object} data  to appear in grid.
60190      */
60191     setSource : function(source){
60192         this.store.setSource(source);
60193         //this.autoSize();
60194     },
60195     /**
60196      * Gets all the data from the grid.
60197      * @return {Object} data  data stored in grid
60198      */
60199     getSource : function(){
60200         return this.store.getSource();
60201     }
60202 });/*
60203   
60204  * Licence LGPL
60205  
60206  */
60207  
60208 /**
60209  * @class Roo.grid.Calendar
60210  * @extends Roo.util.Grid
60211  * This class extends the Grid to provide a calendar widget
60212  * <br><br>Usage:<pre><code>
60213  var grid = new Roo.grid.Calendar("my-container-id", {
60214      ds: myDataStore,
60215      cm: myColModel,
60216      selModel: mySelectionModel,
60217      autoSizeColumns: true,
60218      monitorWindowResize: false,
60219      trackMouseOver: true
60220      eventstore : real data store..
60221  });
60222  // set any options
60223  grid.render();
60224   
60225   * @constructor
60226  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60227  * The container MUST have some type of size defined for the grid to fill. The container will be
60228  * automatically set to position relative if it isn't already.
60229  * @param {Object} config A config object that sets properties on this grid.
60230  */
60231 Roo.grid.Calendar = function(container, config){
60232         // initialize the container
60233         this.container = Roo.get(container);
60234         this.container.update("");
60235         this.container.setStyle("overflow", "hidden");
60236     this.container.addClass('x-grid-container');
60237
60238     this.id = this.container.id;
60239
60240     Roo.apply(this, config);
60241     // check and correct shorthanded configs
60242     
60243     var rows = [];
60244     var d =1;
60245     for (var r = 0;r < 6;r++) {
60246         
60247         rows[r]=[];
60248         for (var c =0;c < 7;c++) {
60249             rows[r][c]= '';
60250         }
60251     }
60252     if (this.eventStore) {
60253         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60254         this.eventStore.on('load',this.onLoad, this);
60255         this.eventStore.on('beforeload',this.clearEvents, this);
60256          
60257     }
60258     
60259     this.dataSource = new Roo.data.Store({
60260             proxy: new Roo.data.MemoryProxy(rows),
60261             reader: new Roo.data.ArrayReader({}, [
60262                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60263     });
60264
60265     this.dataSource.load();
60266     this.ds = this.dataSource;
60267     this.ds.xmodule = this.xmodule || false;
60268     
60269     
60270     var cellRender = function(v,x,r)
60271     {
60272         return String.format(
60273             '<div class="fc-day  fc-widget-content"><div>' +
60274                 '<div class="fc-event-container"></div>' +
60275                 '<div class="fc-day-number">{0}</div>'+
60276                 
60277                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60278             '</div></div>', v);
60279     
60280     }
60281     
60282     
60283     this.colModel = new Roo.grid.ColumnModel( [
60284         {
60285             xtype: 'ColumnModel',
60286             xns: Roo.grid,
60287             dataIndex : 'weekday0',
60288             header : 'Sunday',
60289             renderer : cellRender
60290         },
60291         {
60292             xtype: 'ColumnModel',
60293             xns: Roo.grid,
60294             dataIndex : 'weekday1',
60295             header : 'Monday',
60296             renderer : cellRender
60297         },
60298         {
60299             xtype: 'ColumnModel',
60300             xns: Roo.grid,
60301             dataIndex : 'weekday2',
60302             header : 'Tuesday',
60303             renderer : cellRender
60304         },
60305         {
60306             xtype: 'ColumnModel',
60307             xns: Roo.grid,
60308             dataIndex : 'weekday3',
60309             header : 'Wednesday',
60310             renderer : cellRender
60311         },
60312         {
60313             xtype: 'ColumnModel',
60314             xns: Roo.grid,
60315             dataIndex : 'weekday4',
60316             header : 'Thursday',
60317             renderer : cellRender
60318         },
60319         {
60320             xtype: 'ColumnModel',
60321             xns: Roo.grid,
60322             dataIndex : 'weekday5',
60323             header : 'Friday',
60324             renderer : cellRender
60325         },
60326         {
60327             xtype: 'ColumnModel',
60328             xns: Roo.grid,
60329             dataIndex : 'weekday6',
60330             header : 'Saturday',
60331             renderer : cellRender
60332         }
60333     ]);
60334     this.cm = this.colModel;
60335     this.cm.xmodule = this.xmodule || false;
60336  
60337         
60338           
60339     //this.selModel = new Roo.grid.CellSelectionModel();
60340     //this.sm = this.selModel;
60341     //this.selModel.init(this);
60342     
60343     
60344     if(this.width){
60345         this.container.setWidth(this.width);
60346     }
60347
60348     if(this.height){
60349         this.container.setHeight(this.height);
60350     }
60351     /** @private */
60352         this.addEvents({
60353         // raw events
60354         /**
60355          * @event click
60356          * The raw click event for the entire grid.
60357          * @param {Roo.EventObject} e
60358          */
60359         "click" : true,
60360         /**
60361          * @event dblclick
60362          * The raw dblclick event for the entire grid.
60363          * @param {Roo.EventObject} e
60364          */
60365         "dblclick" : true,
60366         /**
60367          * @event contextmenu
60368          * The raw contextmenu event for the entire grid.
60369          * @param {Roo.EventObject} e
60370          */
60371         "contextmenu" : true,
60372         /**
60373          * @event mousedown
60374          * The raw mousedown event for the entire grid.
60375          * @param {Roo.EventObject} e
60376          */
60377         "mousedown" : true,
60378         /**
60379          * @event mouseup
60380          * The raw mouseup event for the entire grid.
60381          * @param {Roo.EventObject} e
60382          */
60383         "mouseup" : true,
60384         /**
60385          * @event mouseover
60386          * The raw mouseover event for the entire grid.
60387          * @param {Roo.EventObject} e
60388          */
60389         "mouseover" : true,
60390         /**
60391          * @event mouseout
60392          * The raw mouseout event for the entire grid.
60393          * @param {Roo.EventObject} e
60394          */
60395         "mouseout" : true,
60396         /**
60397          * @event keypress
60398          * The raw keypress event for the entire grid.
60399          * @param {Roo.EventObject} e
60400          */
60401         "keypress" : true,
60402         /**
60403          * @event keydown
60404          * The raw keydown event for the entire grid.
60405          * @param {Roo.EventObject} e
60406          */
60407         "keydown" : true,
60408
60409         // custom events
60410
60411         /**
60412          * @event cellclick
60413          * Fires when a cell is clicked
60414          * @param {Grid} this
60415          * @param {Number} rowIndex
60416          * @param {Number} columnIndex
60417          * @param {Roo.EventObject} e
60418          */
60419         "cellclick" : true,
60420         /**
60421          * @event celldblclick
60422          * Fires when a cell is double clicked
60423          * @param {Grid} this
60424          * @param {Number} rowIndex
60425          * @param {Number} columnIndex
60426          * @param {Roo.EventObject} e
60427          */
60428         "celldblclick" : true,
60429         /**
60430          * @event rowclick
60431          * Fires when a row is clicked
60432          * @param {Grid} this
60433          * @param {Number} rowIndex
60434          * @param {Roo.EventObject} e
60435          */
60436         "rowclick" : true,
60437         /**
60438          * @event rowdblclick
60439          * Fires when a row is double clicked
60440          * @param {Grid} this
60441          * @param {Number} rowIndex
60442          * @param {Roo.EventObject} e
60443          */
60444         "rowdblclick" : true,
60445         /**
60446          * @event headerclick
60447          * Fires when a header is clicked
60448          * @param {Grid} this
60449          * @param {Number} columnIndex
60450          * @param {Roo.EventObject} e
60451          */
60452         "headerclick" : true,
60453         /**
60454          * @event headerdblclick
60455          * Fires when a header cell is double clicked
60456          * @param {Grid} this
60457          * @param {Number} columnIndex
60458          * @param {Roo.EventObject} e
60459          */
60460         "headerdblclick" : true,
60461         /**
60462          * @event rowcontextmenu
60463          * Fires when a row is right clicked
60464          * @param {Grid} this
60465          * @param {Number} rowIndex
60466          * @param {Roo.EventObject} e
60467          */
60468         "rowcontextmenu" : true,
60469         /**
60470          * @event cellcontextmenu
60471          * Fires when a cell is right clicked
60472          * @param {Grid} this
60473          * @param {Number} rowIndex
60474          * @param {Number} cellIndex
60475          * @param {Roo.EventObject} e
60476          */
60477          "cellcontextmenu" : true,
60478         /**
60479          * @event headercontextmenu
60480          * Fires when a header is right clicked
60481          * @param {Grid} this
60482          * @param {Number} columnIndex
60483          * @param {Roo.EventObject} e
60484          */
60485         "headercontextmenu" : true,
60486         /**
60487          * @event bodyscroll
60488          * Fires when the body element is scrolled
60489          * @param {Number} scrollLeft
60490          * @param {Number} scrollTop
60491          */
60492         "bodyscroll" : true,
60493         /**
60494          * @event columnresize
60495          * Fires when the user resizes a column
60496          * @param {Number} columnIndex
60497          * @param {Number} newSize
60498          */
60499         "columnresize" : true,
60500         /**
60501          * @event columnmove
60502          * Fires when the user moves a column
60503          * @param {Number} oldIndex
60504          * @param {Number} newIndex
60505          */
60506         "columnmove" : true,
60507         /**
60508          * @event startdrag
60509          * Fires when row(s) start being dragged
60510          * @param {Grid} this
60511          * @param {Roo.GridDD} dd The drag drop object
60512          * @param {event} e The raw browser event
60513          */
60514         "startdrag" : true,
60515         /**
60516          * @event enddrag
60517          * Fires when a drag operation is complete
60518          * @param {Grid} this
60519          * @param {Roo.GridDD} dd The drag drop object
60520          * @param {event} e The raw browser event
60521          */
60522         "enddrag" : true,
60523         /**
60524          * @event dragdrop
60525          * Fires when dragged row(s) are dropped on a valid DD target
60526          * @param {Grid} this
60527          * @param {Roo.GridDD} dd The drag drop object
60528          * @param {String} targetId The target drag drop object
60529          * @param {event} e The raw browser event
60530          */
60531         "dragdrop" : true,
60532         /**
60533          * @event dragover
60534          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60535          * @param {Grid} this
60536          * @param {Roo.GridDD} dd The drag drop object
60537          * @param {String} targetId The target drag drop object
60538          * @param {event} e The raw browser event
60539          */
60540         "dragover" : true,
60541         /**
60542          * @event dragenter
60543          *  Fires when the dragged row(s) first cross another DD target while being dragged
60544          * @param {Grid} this
60545          * @param {Roo.GridDD} dd The drag drop object
60546          * @param {String} targetId The target drag drop object
60547          * @param {event} e The raw browser event
60548          */
60549         "dragenter" : true,
60550         /**
60551          * @event dragout
60552          * Fires when the dragged row(s) leave another DD target while being dragged
60553          * @param {Grid} this
60554          * @param {Roo.GridDD} dd The drag drop object
60555          * @param {String} targetId The target drag drop object
60556          * @param {event} e The raw browser event
60557          */
60558         "dragout" : true,
60559         /**
60560          * @event rowclass
60561          * Fires when a row is rendered, so you can change add a style to it.
60562          * @param {GridView} gridview   The grid view
60563          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60564          */
60565         'rowclass' : true,
60566
60567         /**
60568          * @event render
60569          * Fires when the grid is rendered
60570          * @param {Grid} grid
60571          */
60572         'render' : true,
60573             /**
60574              * @event select
60575              * Fires when a date is selected
60576              * @param {DatePicker} this
60577              * @param {Date} date The selected date
60578              */
60579         'select': true,
60580         /**
60581              * @event monthchange
60582              * Fires when the displayed month changes 
60583              * @param {DatePicker} this
60584              * @param {Date} date The selected month
60585              */
60586         'monthchange': true,
60587         /**
60588              * @event evententer
60589              * Fires when mouse over an event
60590              * @param {Calendar} this
60591              * @param {event} Event
60592              */
60593         'evententer': true,
60594         /**
60595              * @event eventleave
60596              * Fires when the mouse leaves an
60597              * @param {Calendar} this
60598              * @param {event}
60599              */
60600         'eventleave': true,
60601         /**
60602              * @event eventclick
60603              * Fires when the mouse click an
60604              * @param {Calendar} this
60605              * @param {event}
60606              */
60607         'eventclick': true,
60608         /**
60609              * @event eventrender
60610              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60611              * @param {Calendar} this
60612              * @param {data} data to be modified
60613              */
60614         'eventrender': true
60615         
60616     });
60617
60618     Roo.grid.Grid.superclass.constructor.call(this);
60619     this.on('render', function() {
60620         this.view.el.addClass('x-grid-cal'); 
60621         
60622         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60623
60624     },this);
60625     
60626     if (!Roo.grid.Calendar.style) {
60627         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60628             
60629             
60630             '.x-grid-cal .x-grid-col' :  {
60631                 height: 'auto !important',
60632                 'vertical-align': 'top'
60633             },
60634             '.x-grid-cal  .fc-event-hori' : {
60635                 height: '14px'
60636             }
60637              
60638             
60639         }, Roo.id());
60640     }
60641
60642     
60643     
60644 };
60645 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60646     /**
60647      * @cfg {Store} eventStore The store that loads events.
60648      */
60649     eventStore : 25,
60650
60651      
60652     activeDate : false,
60653     startDay : 0,
60654     autoWidth : true,
60655     monitorWindowResize : false,
60656
60657     
60658     resizeColumns : function() {
60659         var col = (this.view.el.getWidth() / 7) - 3;
60660         // loop through cols, and setWidth
60661         for(var i =0 ; i < 7 ; i++){
60662             this.cm.setColumnWidth(i, col);
60663         }
60664     },
60665      setDate :function(date) {
60666         
60667         Roo.log('setDate?');
60668         
60669         this.resizeColumns();
60670         var vd = this.activeDate;
60671         this.activeDate = date;
60672 //        if(vd && this.el){
60673 //            var t = date.getTime();
60674 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60675 //                Roo.log('using add remove');
60676 //                
60677 //                this.fireEvent('monthchange', this, date);
60678 //                
60679 //                this.cells.removeClass("fc-state-highlight");
60680 //                this.cells.each(function(c){
60681 //                   if(c.dateValue == t){
60682 //                       c.addClass("fc-state-highlight");
60683 //                       setTimeout(function(){
60684 //                            try{c.dom.firstChild.focus();}catch(e){}
60685 //                       }, 50);
60686 //                       return false;
60687 //                   }
60688 //                   return true;
60689 //                });
60690 //                return;
60691 //            }
60692 //        }
60693         
60694         var days = date.getDaysInMonth();
60695         
60696         var firstOfMonth = date.getFirstDateOfMonth();
60697         var startingPos = firstOfMonth.getDay()-this.startDay;
60698         
60699         if(startingPos < this.startDay){
60700             startingPos += 7;
60701         }
60702         
60703         var pm = date.add(Date.MONTH, -1);
60704         var prevStart = pm.getDaysInMonth()-startingPos;
60705 //        
60706         
60707         
60708         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60709         
60710         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60711         //this.cells.addClassOnOver('fc-state-hover');
60712         
60713         var cells = this.cells.elements;
60714         var textEls = this.textNodes;
60715         
60716         //Roo.each(cells, function(cell){
60717         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60718         //});
60719         
60720         days += startingPos;
60721
60722         // convert everything to numbers so it's fast
60723         var day = 86400000;
60724         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60725         //Roo.log(d);
60726         //Roo.log(pm);
60727         //Roo.log(prevStart);
60728         
60729         var today = new Date().clearTime().getTime();
60730         var sel = date.clearTime().getTime();
60731         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60732         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60733         var ddMatch = this.disabledDatesRE;
60734         var ddText = this.disabledDatesText;
60735         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60736         var ddaysText = this.disabledDaysText;
60737         var format = this.format;
60738         
60739         var setCellClass = function(cal, cell){
60740             
60741             //Roo.log('set Cell Class');
60742             cell.title = "";
60743             var t = d.getTime();
60744             
60745             //Roo.log(d);
60746             
60747             
60748             cell.dateValue = t;
60749             if(t == today){
60750                 cell.className += " fc-today";
60751                 cell.className += " fc-state-highlight";
60752                 cell.title = cal.todayText;
60753             }
60754             if(t == sel){
60755                 // disable highlight in other month..
60756                 cell.className += " fc-state-highlight";
60757                 
60758             }
60759             // disabling
60760             if(t < min) {
60761                 //cell.className = " fc-state-disabled";
60762                 cell.title = cal.minText;
60763                 return;
60764             }
60765             if(t > max) {
60766                 //cell.className = " fc-state-disabled";
60767                 cell.title = cal.maxText;
60768                 return;
60769             }
60770             if(ddays){
60771                 if(ddays.indexOf(d.getDay()) != -1){
60772                     // cell.title = ddaysText;
60773                    // cell.className = " fc-state-disabled";
60774                 }
60775             }
60776             if(ddMatch && format){
60777                 var fvalue = d.dateFormat(format);
60778                 if(ddMatch.test(fvalue)){
60779                     cell.title = ddText.replace("%0", fvalue);
60780                    cell.className = " fc-state-disabled";
60781                 }
60782             }
60783             
60784             if (!cell.initialClassName) {
60785                 cell.initialClassName = cell.dom.className;
60786             }
60787             
60788             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60789         };
60790
60791         var i = 0;
60792         
60793         for(; i < startingPos; i++) {
60794             cells[i].dayName =  (++prevStart);
60795             Roo.log(textEls[i]);
60796             d.setDate(d.getDate()+1);
60797             
60798             //cells[i].className = "fc-past fc-other-month";
60799             setCellClass(this, cells[i]);
60800         }
60801         
60802         var intDay = 0;
60803         
60804         for(; i < days; i++){
60805             intDay = i - startingPos + 1;
60806             cells[i].dayName =  (intDay);
60807             d.setDate(d.getDate()+1);
60808             
60809             cells[i].className = ''; // "x-date-active";
60810             setCellClass(this, cells[i]);
60811         }
60812         var extraDays = 0;
60813         
60814         for(; i < 42; i++) {
60815             //textEls[i].innerHTML = (++extraDays);
60816             
60817             d.setDate(d.getDate()+1);
60818             cells[i].dayName = (++extraDays);
60819             cells[i].className = "fc-future fc-other-month";
60820             setCellClass(this, cells[i]);
60821         }
60822         
60823         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60824         
60825         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60826         
60827         // this will cause all the cells to mis
60828         var rows= [];
60829         var i =0;
60830         for (var r = 0;r < 6;r++) {
60831             for (var c =0;c < 7;c++) {
60832                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60833             }    
60834         }
60835         
60836         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60837         for(i=0;i<cells.length;i++) {
60838             
60839             this.cells.elements[i].dayName = cells[i].dayName ;
60840             this.cells.elements[i].className = cells[i].className;
60841             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60842             this.cells.elements[i].title = cells[i].title ;
60843             this.cells.elements[i].dateValue = cells[i].dateValue ;
60844         }
60845         
60846         
60847         
60848         
60849         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60850         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60851         
60852         ////if(totalRows != 6){
60853             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60854            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60855        // }
60856         
60857         this.fireEvent('monthchange', this, date);
60858         
60859         
60860     },
60861  /**
60862      * Returns the grid's SelectionModel.
60863      * @return {SelectionModel}
60864      */
60865     getSelectionModel : function(){
60866         if(!this.selModel){
60867             this.selModel = new Roo.grid.CellSelectionModel();
60868         }
60869         return this.selModel;
60870     },
60871
60872     load: function() {
60873         this.eventStore.load()
60874         
60875         
60876         
60877     },
60878     
60879     findCell : function(dt) {
60880         dt = dt.clearTime().getTime();
60881         var ret = false;
60882         this.cells.each(function(c){
60883             //Roo.log("check " +c.dateValue + '?=' + dt);
60884             if(c.dateValue == dt){
60885                 ret = c;
60886                 return false;
60887             }
60888             return true;
60889         });
60890         
60891         return ret;
60892     },
60893     
60894     findCells : function(rec) {
60895         var s = rec.data.start_dt.clone().clearTime().getTime();
60896        // Roo.log(s);
60897         var e= rec.data.end_dt.clone().clearTime().getTime();
60898        // Roo.log(e);
60899         var ret = [];
60900         this.cells.each(function(c){
60901              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60902             
60903             if(c.dateValue > e){
60904                 return ;
60905             }
60906             if(c.dateValue < s){
60907                 return ;
60908             }
60909             ret.push(c);
60910         });
60911         
60912         return ret;    
60913     },
60914     
60915     findBestRow: function(cells)
60916     {
60917         var ret = 0;
60918         
60919         for (var i =0 ; i < cells.length;i++) {
60920             ret  = Math.max(cells[i].rows || 0,ret);
60921         }
60922         return ret;
60923         
60924     },
60925     
60926     
60927     addItem : function(rec)
60928     {
60929         // look for vertical location slot in
60930         var cells = this.findCells(rec);
60931         
60932         rec.row = this.findBestRow(cells);
60933         
60934         // work out the location.
60935         
60936         var crow = false;
60937         var rows = [];
60938         for(var i =0; i < cells.length; i++) {
60939             if (!crow) {
60940                 crow = {
60941                     start : cells[i],
60942                     end :  cells[i]
60943                 };
60944                 continue;
60945             }
60946             if (crow.start.getY() == cells[i].getY()) {
60947                 // on same row.
60948                 crow.end = cells[i];
60949                 continue;
60950             }
60951             // different row.
60952             rows.push(crow);
60953             crow = {
60954                 start: cells[i],
60955                 end : cells[i]
60956             };
60957             
60958         }
60959         
60960         rows.push(crow);
60961         rec.els = [];
60962         rec.rows = rows;
60963         rec.cells = cells;
60964         for (var i = 0; i < cells.length;i++) {
60965             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60966             
60967         }
60968         
60969         
60970     },
60971     
60972     clearEvents: function() {
60973         
60974         if (!this.eventStore.getCount()) {
60975             return;
60976         }
60977         // reset number of rows in cells.
60978         Roo.each(this.cells.elements, function(c){
60979             c.rows = 0;
60980         });
60981         
60982         this.eventStore.each(function(e) {
60983             this.clearEvent(e);
60984         },this);
60985         
60986     },
60987     
60988     clearEvent : function(ev)
60989     {
60990         if (ev.els) {
60991             Roo.each(ev.els, function(el) {
60992                 el.un('mouseenter' ,this.onEventEnter, this);
60993                 el.un('mouseleave' ,this.onEventLeave, this);
60994                 el.remove();
60995             },this);
60996             ev.els = [];
60997         }
60998     },
60999     
61000     
61001     renderEvent : function(ev,ctr) {
61002         if (!ctr) {
61003              ctr = this.view.el.select('.fc-event-container',true).first();
61004         }
61005         
61006          
61007         this.clearEvent(ev);
61008             //code
61009        
61010         
61011         
61012         ev.els = [];
61013         var cells = ev.cells;
61014         var rows = ev.rows;
61015         this.fireEvent('eventrender', this, ev);
61016         
61017         for(var i =0; i < rows.length; i++) {
61018             
61019             cls = '';
61020             if (i == 0) {
61021                 cls += ' fc-event-start';
61022             }
61023             if ((i+1) == rows.length) {
61024                 cls += ' fc-event-end';
61025             }
61026             
61027             //Roo.log(ev.data);
61028             // how many rows should it span..
61029             var cg = this.eventTmpl.append(ctr,Roo.apply({
61030                 fccls : cls
61031                 
61032             }, ev.data) , true);
61033             
61034             
61035             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61036             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61037             cg.on('click', this.onEventClick, this, ev);
61038             
61039             ev.els.push(cg);
61040             
61041             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61042             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61043             //Roo.log(cg);
61044              
61045             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61046             cg.setWidth(ebox.right - sbox.x -2);
61047         }
61048     },
61049     
61050     renderEvents: function()
61051     {   
61052         // first make sure there is enough space..
61053         
61054         if (!this.eventTmpl) {
61055             this.eventTmpl = new Roo.Template(
61056                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61057                     '<div class="fc-event-inner">' +
61058                         '<span class="fc-event-time">{time}</span>' +
61059                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61060                     '</div>' +
61061                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61062                 '</div>'
61063             );
61064                 
61065         }
61066                
61067         
61068         
61069         this.cells.each(function(c) {
61070             //Roo.log(c.select('.fc-day-content div',true).first());
61071             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61072         });
61073         
61074         var ctr = this.view.el.select('.fc-event-container',true).first();
61075         
61076         var cls;
61077         this.eventStore.each(function(ev){
61078             
61079             this.renderEvent(ev);
61080              
61081              
61082         }, this);
61083         this.view.layout();
61084         
61085     },
61086     
61087     onEventEnter: function (e, el,event,d) {
61088         this.fireEvent('evententer', this, el, event);
61089     },
61090     
61091     onEventLeave: function (e, el,event,d) {
61092         this.fireEvent('eventleave', this, el, event);
61093     },
61094     
61095     onEventClick: function (e, el,event,d) {
61096         this.fireEvent('eventclick', this, el, event);
61097     },
61098     
61099     onMonthChange: function () {
61100         this.store.load();
61101     },
61102     
61103     onLoad: function () {
61104         
61105         //Roo.log('calendar onload');
61106 //         
61107         if(this.eventStore.getCount() > 0){
61108             
61109            
61110             
61111             this.eventStore.each(function(d){
61112                 
61113                 
61114                 // FIXME..
61115                 var add =   d.data;
61116                 if (typeof(add.end_dt) == 'undefined')  {
61117                     Roo.log("Missing End time in calendar data: ");
61118                     Roo.log(d);
61119                     return;
61120                 }
61121                 if (typeof(add.start_dt) == 'undefined')  {
61122                     Roo.log("Missing Start time in calendar data: ");
61123                     Roo.log(d);
61124                     return;
61125                 }
61126                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61127                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61128                 add.id = add.id || d.id;
61129                 add.title = add.title || '??';
61130                 
61131                 this.addItem(d);
61132                 
61133              
61134             },this);
61135         }
61136         
61137         this.renderEvents();
61138     }
61139     
61140
61141 });
61142 /*
61143  grid : {
61144                 xtype: 'Grid',
61145                 xns: Roo.grid,
61146                 listeners : {
61147                     render : function ()
61148                     {
61149                         _this.grid = this;
61150                         
61151                         if (!this.view.el.hasClass('course-timesheet')) {
61152                             this.view.el.addClass('course-timesheet');
61153                         }
61154                         if (this.tsStyle) {
61155                             this.ds.load({});
61156                             return; 
61157                         }
61158                         Roo.log('width');
61159                         Roo.log(_this.grid.view.el.getWidth());
61160                         
61161                         
61162                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61163                             '.course-timesheet .x-grid-row' : {
61164                                 height: '80px'
61165                             },
61166                             '.x-grid-row td' : {
61167                                 'vertical-align' : 0
61168                             },
61169                             '.course-edit-link' : {
61170                                 'color' : 'blue',
61171                                 'text-overflow' : 'ellipsis',
61172                                 'overflow' : 'hidden',
61173                                 'white-space' : 'nowrap',
61174                                 'cursor' : 'pointer'
61175                             },
61176                             '.sub-link' : {
61177                                 'color' : 'green'
61178                             },
61179                             '.de-act-sup-link' : {
61180                                 'color' : 'purple',
61181                                 'text-decoration' : 'line-through'
61182                             },
61183                             '.de-act-link' : {
61184                                 'color' : 'red',
61185                                 'text-decoration' : 'line-through'
61186                             },
61187                             '.course-timesheet .course-highlight' : {
61188                                 'border-top-style': 'dashed !important',
61189                                 'border-bottom-bottom': 'dashed !important'
61190                             },
61191                             '.course-timesheet .course-item' : {
61192                                 'font-family'   : 'tahoma, arial, helvetica',
61193                                 'font-size'     : '11px',
61194                                 'overflow'      : 'hidden',
61195                                 'padding-left'  : '10px',
61196                                 'padding-right' : '10px',
61197                                 'padding-top' : '10px' 
61198                             }
61199                             
61200                         }, Roo.id());
61201                                 this.ds.load({});
61202                     }
61203                 },
61204                 autoWidth : true,
61205                 monitorWindowResize : false,
61206                 cellrenderer : function(v,x,r)
61207                 {
61208                     return v;
61209                 },
61210                 sm : {
61211                     xtype: 'CellSelectionModel',
61212                     xns: Roo.grid
61213                 },
61214                 dataSource : {
61215                     xtype: 'Store',
61216                     xns: Roo.data,
61217                     listeners : {
61218                         beforeload : function (_self, options)
61219                         {
61220                             options.params = options.params || {};
61221                             options.params._month = _this.monthField.getValue();
61222                             options.params.limit = 9999;
61223                             options.params['sort'] = 'when_dt';    
61224                             options.params['dir'] = 'ASC';    
61225                             this.proxy.loadResponse = this.loadResponse;
61226                             Roo.log("load?");
61227                             //this.addColumns();
61228                         },
61229                         load : function (_self, records, options)
61230                         {
61231                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61232                                 // if you click on the translation.. you can edit it...
61233                                 var el = Roo.get(this);
61234                                 var id = el.dom.getAttribute('data-id');
61235                                 var d = el.dom.getAttribute('data-date');
61236                                 var t = el.dom.getAttribute('data-time');
61237                                 //var id = this.child('span').dom.textContent;
61238                                 
61239                                 //Roo.log(this);
61240                                 Pman.Dialog.CourseCalendar.show({
61241                                     id : id,
61242                                     when_d : d,
61243                                     when_t : t,
61244                                     productitem_active : id ? 1 : 0
61245                                 }, function() {
61246                                     _this.grid.ds.load({});
61247                                 });
61248                            
61249                            });
61250                            
61251                            _this.panel.fireEvent('resize', [ '', '' ]);
61252                         }
61253                     },
61254                     loadResponse : function(o, success, response){
61255                             // this is overridden on before load..
61256                             
61257                             Roo.log("our code?");       
61258                             //Roo.log(success);
61259                             //Roo.log(response)
61260                             delete this.activeRequest;
61261                             if(!success){
61262                                 this.fireEvent("loadexception", this, o, response);
61263                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61264                                 return;
61265                             }
61266                             var result;
61267                             try {
61268                                 result = o.reader.read(response);
61269                             }catch(e){
61270                                 Roo.log("load exception?");
61271                                 this.fireEvent("loadexception", this, o, response, e);
61272                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61273                                 return;
61274                             }
61275                             Roo.log("ready...");        
61276                             // loop through result.records;
61277                             // and set this.tdate[date] = [] << array of records..
61278                             _this.tdata  = {};
61279                             Roo.each(result.records, function(r){
61280                                 //Roo.log(r.data);
61281                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61282                                     _this.tdata[r.data.when_dt.format('j')] = [];
61283                                 }
61284                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61285                             });
61286                             
61287                             //Roo.log(_this.tdata);
61288                             
61289                             result.records = [];
61290                             result.totalRecords = 6;
61291                     
61292                             // let's generate some duumy records for the rows.
61293                             //var st = _this.dateField.getValue();
61294                             
61295                             // work out monday..
61296                             //st = st.add(Date.DAY, -1 * st.format('w'));
61297                             
61298                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61299                             
61300                             var firstOfMonth = date.getFirstDayOfMonth();
61301                             var days = date.getDaysInMonth();
61302                             var d = 1;
61303                             var firstAdded = false;
61304                             for (var i = 0; i < result.totalRecords ; i++) {
61305                                 //var d= st.add(Date.DAY, i);
61306                                 var row = {};
61307                                 var added = 0;
61308                                 for(var w = 0 ; w < 7 ; w++){
61309                                     if(!firstAdded && firstOfMonth != w){
61310                                         continue;
61311                                     }
61312                                     if(d > days){
61313                                         continue;
61314                                     }
61315                                     firstAdded = true;
61316                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61317                                     row['weekday'+w] = String.format(
61318                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61319                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61320                                                     d,
61321                                                     date.format('Y-m-')+dd
61322                                                 );
61323                                     added++;
61324                                     if(typeof(_this.tdata[d]) != 'undefined'){
61325                                         Roo.each(_this.tdata[d], function(r){
61326                                             var is_sub = '';
61327                                             var deactive = '';
61328                                             var id = r.id;
61329                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61330                                             if(r.parent_id*1>0){
61331                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61332                                                 id = r.parent_id;
61333                                             }
61334                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61335                                                 deactive = 'de-act-link';
61336                                             }
61337                                             
61338                                             row['weekday'+w] += String.format(
61339                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61340                                                     id, //0
61341                                                     r.product_id_name, //1
61342                                                     r.when_dt.format('h:ia'), //2
61343                                                     is_sub, //3
61344                                                     deactive, //4
61345                                                     desc // 5
61346                                             );
61347                                         });
61348                                     }
61349                                     d++;
61350                                 }
61351                                 
61352                                 // only do this if something added..
61353                                 if(added > 0){ 
61354                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61355                                 }
61356                                 
61357                                 
61358                                 // push it twice. (second one with an hour..
61359                                 
61360                             }
61361                             //Roo.log(result);
61362                             this.fireEvent("load", this, o, o.request.arg);
61363                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61364                         },
61365                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61366                     proxy : {
61367                         xtype: 'HttpProxy',
61368                         xns: Roo.data,
61369                         method : 'GET',
61370                         url : baseURL + '/Roo/Shop_course.php'
61371                     },
61372                     reader : {
61373                         xtype: 'JsonReader',
61374                         xns: Roo.data,
61375                         id : 'id',
61376                         fields : [
61377                             {
61378                                 'name': 'id',
61379                                 'type': 'int'
61380                             },
61381                             {
61382                                 'name': 'when_dt',
61383                                 'type': 'string'
61384                             },
61385                             {
61386                                 'name': 'end_dt',
61387                                 'type': 'string'
61388                             },
61389                             {
61390                                 'name': 'parent_id',
61391                                 'type': 'int'
61392                             },
61393                             {
61394                                 'name': 'product_id',
61395                                 'type': 'int'
61396                             },
61397                             {
61398                                 'name': 'productitem_id',
61399                                 'type': 'int'
61400                             },
61401                             {
61402                                 'name': 'guid',
61403                                 'type': 'int'
61404                             }
61405                         ]
61406                     }
61407                 },
61408                 toolbar : {
61409                     xtype: 'Toolbar',
61410                     xns: Roo,
61411                     items : [
61412                         {
61413                             xtype: 'Button',
61414                             xns: Roo.Toolbar,
61415                             listeners : {
61416                                 click : function (_self, e)
61417                                 {
61418                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61419                                     sd.setMonth(sd.getMonth()-1);
61420                                     _this.monthField.setValue(sd.format('Y-m-d'));
61421                                     _this.grid.ds.load({});
61422                                 }
61423                             },
61424                             text : "Back"
61425                         },
61426                         {
61427                             xtype: 'Separator',
61428                             xns: Roo.Toolbar
61429                         },
61430                         {
61431                             xtype: 'MonthField',
61432                             xns: Roo.form,
61433                             listeners : {
61434                                 render : function (_self)
61435                                 {
61436                                     _this.monthField = _self;
61437                                    // _this.monthField.set  today
61438                                 },
61439                                 select : function (combo, date)
61440                                 {
61441                                     _this.grid.ds.load({});
61442                                 }
61443                             },
61444                             value : (function() { return new Date(); })()
61445                         },
61446                         {
61447                             xtype: 'Separator',
61448                             xns: Roo.Toolbar
61449                         },
61450                         {
61451                             xtype: 'TextItem',
61452                             xns: Roo.Toolbar,
61453                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61454                         },
61455                         {
61456                             xtype: 'Fill',
61457                             xns: Roo.Toolbar
61458                         },
61459                         {
61460                             xtype: 'Button',
61461                             xns: Roo.Toolbar,
61462                             listeners : {
61463                                 click : function (_self, e)
61464                                 {
61465                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61466                                     sd.setMonth(sd.getMonth()+1);
61467                                     _this.monthField.setValue(sd.format('Y-m-d'));
61468                                     _this.grid.ds.load({});
61469                                 }
61470                             },
61471                             text : "Next"
61472                         }
61473                     ]
61474                 },
61475                  
61476             }
61477         };
61478         
61479         *//*
61480  * Based on:
61481  * Ext JS Library 1.1.1
61482  * Copyright(c) 2006-2007, Ext JS, LLC.
61483  *
61484  * Originally Released Under LGPL - original licence link has changed is not relivant.
61485  *
61486  * Fork - LGPL
61487  * <script type="text/javascript">
61488  */
61489  
61490 /**
61491  * @class Roo.LoadMask
61492  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61493  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61494  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61495  * element's UpdateManager load indicator and will be destroyed after the initial load.
61496  * @constructor
61497  * Create a new LoadMask
61498  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61499  * @param {Object} config The config object
61500  */
61501 Roo.LoadMask = function(el, config){
61502     this.el = Roo.get(el);
61503     Roo.apply(this, config);
61504     if(this.store){
61505         this.store.on('beforeload', this.onBeforeLoad, this);
61506         this.store.on('load', this.onLoad, this);
61507         this.store.on('loadexception', this.onLoadException, this);
61508         this.removeMask = false;
61509     }else{
61510         var um = this.el.getUpdateManager();
61511         um.showLoadIndicator = false; // disable the default indicator
61512         um.on('beforeupdate', this.onBeforeLoad, this);
61513         um.on('update', this.onLoad, this);
61514         um.on('failure', this.onLoad, this);
61515         this.removeMask = true;
61516     }
61517 };
61518
61519 Roo.LoadMask.prototype = {
61520     /**
61521      * @cfg {Boolean} removeMask
61522      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61523      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61524      */
61525     /**
61526      * @cfg {String} msg
61527      * The text to display in a centered loading message box (defaults to 'Loading...')
61528      */
61529     msg : 'Loading...',
61530     /**
61531      * @cfg {String} msgCls
61532      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61533      */
61534     msgCls : 'x-mask-loading',
61535
61536     /**
61537      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61538      * @type Boolean
61539      */
61540     disabled: false,
61541
61542     /**
61543      * Disables the mask to prevent it from being displayed
61544      */
61545     disable : function(){
61546        this.disabled = true;
61547     },
61548
61549     /**
61550      * Enables the mask so that it can be displayed
61551      */
61552     enable : function(){
61553         this.disabled = false;
61554     },
61555     
61556     onLoadException : function()
61557     {
61558         Roo.log(arguments);
61559         
61560         if (typeof(arguments[3]) != 'undefined') {
61561             Roo.MessageBox.alert("Error loading",arguments[3]);
61562         } 
61563         /*
61564         try {
61565             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61566                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61567             }   
61568         } catch(e) {
61569             
61570         }
61571         */
61572     
61573         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61574     },
61575     // private
61576     onLoad : function()
61577     {
61578         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61579     },
61580
61581     // private
61582     onBeforeLoad : function(){
61583         if(!this.disabled){
61584             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61585         }
61586     },
61587
61588     // private
61589     destroy : function(){
61590         if(this.store){
61591             this.store.un('beforeload', this.onBeforeLoad, this);
61592             this.store.un('load', this.onLoad, this);
61593             this.store.un('loadexception', this.onLoadException, this);
61594         }else{
61595             var um = this.el.getUpdateManager();
61596             um.un('beforeupdate', this.onBeforeLoad, this);
61597             um.un('update', this.onLoad, this);
61598             um.un('failure', this.onLoad, this);
61599         }
61600     }
61601 };/*
61602  * Based on:
61603  * Ext JS Library 1.1.1
61604  * Copyright(c) 2006-2007, Ext JS, LLC.
61605  *
61606  * Originally Released Under LGPL - original licence link has changed is not relivant.
61607  *
61608  * Fork - LGPL
61609  * <script type="text/javascript">
61610  */
61611
61612
61613 /**
61614  * @class Roo.XTemplate
61615  * @extends Roo.Template
61616  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61617 <pre><code>
61618 var t = new Roo.XTemplate(
61619         '&lt;select name="{name}"&gt;',
61620                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61621         '&lt;/select&gt;'
61622 );
61623  
61624 // then append, applying the master template values
61625  </code></pre>
61626  *
61627  * Supported features:
61628  *
61629  *  Tags:
61630
61631 <pre><code>
61632       {a_variable} - output encoded.
61633       {a_variable.format:("Y-m-d")} - call a method on the variable
61634       {a_variable:raw} - unencoded output
61635       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61636       {a_variable:this.method_on_template(...)} - call a method on the template object.
61637  
61638 </code></pre>
61639  *  The tpl tag:
61640 <pre><code>
61641         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61642         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61643         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61644         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61645   
61646         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61647         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61648 </code></pre>
61649  *      
61650  */
61651 Roo.XTemplate = function()
61652 {
61653     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61654     if (this.html) {
61655         this.compile();
61656     }
61657 };
61658
61659
61660 Roo.extend(Roo.XTemplate, Roo.Template, {
61661
61662     /**
61663      * The various sub templates
61664      */
61665     tpls : false,
61666     /**
61667      *
61668      * basic tag replacing syntax
61669      * WORD:WORD()
61670      *
61671      * // you can fake an object call by doing this
61672      *  x.t:(test,tesT) 
61673      * 
61674      */
61675     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61676
61677     /**
61678      * compile the template
61679      *
61680      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61681      *
61682      */
61683     compile: function()
61684     {
61685         var s = this.html;
61686      
61687         s = ['<tpl>', s, '</tpl>'].join('');
61688     
61689         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61690             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61691             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61692             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61693             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61694             m,
61695             id     = 0,
61696             tpls   = [];
61697     
61698         while(true == !!(m = s.match(re))){
61699             var forMatch   = m[0].match(nameRe),
61700                 ifMatch   = m[0].match(ifRe),
61701                 execMatch   = m[0].match(execRe),
61702                 namedMatch   = m[0].match(namedRe),
61703                 
61704                 exp  = null, 
61705                 fn   = null,
61706                 exec = null,
61707                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61708                 
61709             if (ifMatch) {
61710                 // if - puts fn into test..
61711                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61712                 if(exp){
61713                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61714                 }
61715             }
61716             
61717             if (execMatch) {
61718                 // exec - calls a function... returns empty if true is  returned.
61719                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61720                 if(exp){
61721                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61722                 }
61723             }
61724             
61725             
61726             if (name) {
61727                 // for = 
61728                 switch(name){
61729                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61730                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61731                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61732                 }
61733             }
61734             var uid = namedMatch ? namedMatch[1] : id;
61735             
61736             
61737             tpls.push({
61738                 id:     namedMatch ? namedMatch[1] : id,
61739                 target: name,
61740                 exec:   exec,
61741                 test:   fn,
61742                 body:   m[1] || ''
61743             });
61744             if (namedMatch) {
61745                 s = s.replace(m[0], '');
61746             } else { 
61747                 s = s.replace(m[0], '{xtpl'+ id + '}');
61748             }
61749             ++id;
61750         }
61751         this.tpls = [];
61752         for(var i = tpls.length-1; i >= 0; --i){
61753             this.compileTpl(tpls[i]);
61754             this.tpls[tpls[i].id] = tpls[i];
61755         }
61756         this.master = tpls[tpls.length-1];
61757         return this;
61758     },
61759     /**
61760      * same as applyTemplate, except it's done to one of the subTemplates
61761      * when using named templates, you can do:
61762      *
61763      * var str = pl.applySubTemplate('your-name', values);
61764      *
61765      * 
61766      * @param {Number} id of the template
61767      * @param {Object} values to apply to template
61768      * @param {Object} parent (normaly the instance of this object)
61769      */
61770     applySubTemplate : function(id, values, parent)
61771     {
61772         
61773         
61774         var t = this.tpls[id];
61775         
61776         
61777         try { 
61778             if(t.test && !t.test.call(this, values, parent)){
61779                 return '';
61780             }
61781         } catch(e) {
61782             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61783             Roo.log(e.toString());
61784             Roo.log(t.test);
61785             return ''
61786         }
61787         try { 
61788             
61789             if(t.exec && t.exec.call(this, values, parent)){
61790                 return '';
61791             }
61792         } catch(e) {
61793             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61794             Roo.log(e.toString());
61795             Roo.log(t.exec);
61796             return ''
61797         }
61798         try {
61799             var vs = t.target ? t.target.call(this, values, parent) : values;
61800             parent = t.target ? values : parent;
61801             if(t.target && vs instanceof Array){
61802                 var buf = [];
61803                 for(var i = 0, len = vs.length; i < len; i++){
61804                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61805                 }
61806                 return buf.join('');
61807             }
61808             return t.compiled.call(this, vs, parent);
61809         } catch (e) {
61810             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61811             Roo.log(e.toString());
61812             Roo.log(t.compiled);
61813             return '';
61814         }
61815     },
61816
61817     compileTpl : function(tpl)
61818     {
61819         var fm = Roo.util.Format;
61820         var useF = this.disableFormats !== true;
61821         var sep = Roo.isGecko ? "+" : ",";
61822         var undef = function(str) {
61823             Roo.log("Property not found :"  + str);
61824             return '';
61825         };
61826         
61827         var fn = function(m, name, format, args)
61828         {
61829             //Roo.log(arguments);
61830             args = args ? args.replace(/\\'/g,"'") : args;
61831             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61832             if (typeof(format) == 'undefined') {
61833                 format= 'htmlEncode';
61834             }
61835             if (format == 'raw' ) {
61836                 format = false;
61837             }
61838             
61839             if(name.substr(0, 4) == 'xtpl'){
61840                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61841             }
61842             
61843             // build an array of options to determine if value is undefined..
61844             
61845             // basically get 'xxxx.yyyy' then do
61846             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61847             //    (function () { Roo.log("Property not found"); return ''; })() :
61848             //    ......
61849             
61850             var udef_ar = [];
61851             var lookfor = '';
61852             Roo.each(name.split('.'), function(st) {
61853                 lookfor += (lookfor.length ? '.': '') + st;
61854                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61855             });
61856             
61857             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61858             
61859             
61860             if(format && useF){
61861                 
61862                 args = args ? ',' + args : "";
61863                  
61864                 if(format.substr(0, 5) != "this."){
61865                     format = "fm." + format + '(';
61866                 }else{
61867                     format = 'this.call("'+ format.substr(5) + '", ';
61868                     args = ", values";
61869                 }
61870                 
61871                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61872             }
61873              
61874             if (args.length) {
61875                 // called with xxyx.yuu:(test,test)
61876                 // change to ()
61877                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61878             }
61879             // raw.. - :raw modifier..
61880             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61881             
61882         };
61883         var body;
61884         // branched to use + in gecko and [].join() in others
61885         if(Roo.isGecko){
61886             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61887                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61888                     "';};};";
61889         }else{
61890             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61891             body.push(tpl.body.replace(/(\r\n|\n)/g,
61892                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61893             body.push("'].join('');};};");
61894             body = body.join('');
61895         }
61896         
61897         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61898        
61899         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61900         eval(body);
61901         
61902         return this;
61903     },
61904
61905     applyTemplate : function(values){
61906         return this.master.compiled.call(this, values, {});
61907         //var s = this.subs;
61908     },
61909
61910     apply : function(){
61911         return this.applyTemplate.apply(this, arguments);
61912     }
61913
61914  });
61915
61916 Roo.XTemplate.from = function(el){
61917     el = Roo.getDom(el);
61918     return new Roo.XTemplate(el.value || el.innerHTML);
61919 };